pipeworks/item_transport.lua

359 lines
11 KiB
Lua
Raw Normal View History

local luaentity = pipeworks.luaentity
local enable_max_limit = minetest.settings:get("pipeworks_enable_items_per_tube_limit")
local max_tube_limit = tonumber(minetest.settings:get("pipeworks_max_items_per_tube")) or 30
if enable_max_limit == nil then enable_max_limit = true end
function pipeworks.tube_item(pos, item)
error("obsolete pipeworks.tube_item() called; change caller to use pipeworks.tube_inject_item() instead")
end
function pipeworks.tube_inject_item(pos, start_pos, velocity, item, owner)
2013-12-15 11:35:11 +01:00
-- Take item in any format
local stack = ItemStack(item)
2014-08-14 16:22:03 +02:00
local obj = luaentity.add_entity(pos, "pipeworks:tubed_item")
obj:set_item(stack:to_string())
obj.start_pos = vector.new(start_pos)
obj:setvelocity(velocity)
obj.owner = owner
2014-08-14 16:22:03 +02:00
--obj:set_color("red") -- todo: this is test-only code
2013-12-15 11:35:11 +01:00
return obj
end
Multiple updates: 1) Refactor autoplace, There was a lot of redundant code and like a dozen unneccessary string scans for every node next to every tube placed! I put it all into indexed tables and loops instead of bizarre and unexplainable variable names and copy and pasted code. There was also no support for notifying a chest when an item has been taken from it by a filter, so I added something for that. I also thought it prudent to fall back on the allow_metadata_inventory_take function should a special can_remove not exist. In fact if can_insert doesn't exist, it calls allow_metadata_inventory_put instead. I also added a thing for allowing pipes to attach to nodes of other modules, without having to hard code type all those node names into autoplace.lua. Basically node.tube.collects(i,param2) and i is the direction from the pipe and param2 is the param2 of what it's pointing at. I also abstracted the inscrutable correlation between i and param2 by trial and error (and the paramwand mod) into understandable functions. There was no pipeworks namespace so I created it, and put these functions into pipeworks.collects (as distinguished from a node.tube.collects function, which uses those functions) And now it's too late to cart my old clothes to the thrift store, dangit. 2) My "node.tube.collects" idea might be redundant with the node.tube.connect_sides thing, though possibly more versatile so I'll leave it in. 3) I was using node.tube.connects and fancy functions for checking if it's the sides or top or whatnot, and this connect_side thing came in. This should make both my way and the way using connect_side work. Also removed some debugging cruft
2013-10-15 05:45:07 +02:00
-- adding two tube functions
-- can_remove(pos,node,stack,dir) returns the maximum number of items of that stack that can be removed
Multiple updates: 1) Refactor autoplace, There was a lot of redundant code and like a dozen unneccessary string scans for every node next to every tube placed! I put it all into indexed tables and loops instead of bizarre and unexplainable variable names and copy and pasted code. There was also no support for notifying a chest when an item has been taken from it by a filter, so I added something for that. I also thought it prudent to fall back on the allow_metadata_inventory_take function should a special can_remove not exist. In fact if can_insert doesn't exist, it calls allow_metadata_inventory_put instead. I also added a thing for allowing pipes to attach to nodes of other modules, without having to hard code type all those node names into autoplace.lua. Basically node.tube.collects(i,param2) and i is the direction from the pipe and param2 is the param2 of what it's pointing at. I also abstracted the inscrutable correlation between i and param2 by trial and error (and the paramwand mod) into understandable functions. There was no pipeworks namespace so I created it, and put these functions into pipeworks.collects (as distinguished from a node.tube.collects function, which uses those functions) And now it's too late to cart my old clothes to the thrift store, dangit. 2) My "node.tube.collects" idea might be redundant with the node.tube.connect_sides thing, though possibly more versatile so I'll leave it in. 3) I was using node.tube.connects and fancy functions for checking if it's the sides or top or whatnot, and this connect_side thing came in. This should make both my way and the way using connect_side work. Also removed some debugging cruft
2013-10-15 05:45:07 +02:00
-- remove_items(pos,node,stack,dir,count) removes count items and returns them
-- both optional w/ sensible defaults and fallback to normal allow_* function
-- XXX: possibly change insert_object to insert_item
local adjlist={{x=0,y=0,z=1},{x=0,y=0,z=-1},{x=0,y=1,z=0},{x=0,y=-1,z=0},{x=1,y=0,z=0},{x=-1,y=0,z=0}}
2013-12-15 11:35:11 +01:00
2013-12-21 10:16:58 +01:00
function pipeworks.notvel(tbl, vel)
2013-12-15 11:35:11 +01:00
local tbl2={}
for _,val in ipairs(tbl) do
2013-12-21 10:16:58 +01:00
if val.x ~= -vel.x or val.y ~= -vel.y or val.z ~= -vel.z then table.insert(tbl2, val) end
2013-12-15 11:35:11 +01:00
end
return tbl2
end
local tube_item_count = {}
minetest.register_globalstep(function(dtime)
if not luaentity.entities then
return
end
tube_item_count = {}
for id, entity in pairs(luaentity.entities) do
if entity.name == "pipeworks:tubed_item" then
local h = minetest.hash_node_position(vector.round(entity._pos))
tube_item_count[h] = (tube_item_count[h] or 0) + 1
end
end
end)
-- tube overload mechanism:
-- when the tube's item count (tracked in the above tube_item_count table)
-- exceeds the limit configured per tube, replace it with a broken one.
local crunch_tube = function(pos, cnode, cmeta)
if enable_max_limit then
local h = minetest.hash_node_position(pos)
local itemcount = tube_item_count[h] or 0
if itemcount > max_tube_limit then
cmeta:set_string("the_tube_was", minetest.serialize(cnode))
print("[Pipeworks] Warning - a tube at "..minetest.pos_to_string(pos).." broke due to too many items ("..itemcount..")")
minetest.swap_node(pos, {name = "pipeworks:broken_tube_1"})
pipeworks.scan_for_tube_objects(pos)
end
end
end
local function go_next(pos, velocity, stack, owner)
2014-08-14 16:22:03 +02:00
local next_positions = {}
local max_priority = 0
2013-12-21 08:46:55 +01:00
local cnode = minetest.get_node(pos)
local cmeta = minetest.get_meta(pos)
2013-12-15 11:35:11 +01:00
local can_go
2013-12-21 10:16:58 +01:00
local speed = math.abs(velocity.x + velocity.y + velocity.z)
2014-08-14 16:22:03 +02:00
if speed == 0 then
speed = 1
end
2013-12-21 10:16:58 +01:00
local vel = {x = velocity.x/speed, y = velocity.y/speed, z = velocity.z/speed,speed=speed}
2013-12-21 08:46:55 +01:00
if speed >= 4.1 then
speed = 4
elseif speed >= 1.1 then
2014-08-14 16:22:03 +02:00
speed = speed - 0.1
2013-12-15 11:35:11 +01:00
else
2013-12-21 08:46:55 +01:00
speed = 1
2013-12-15 11:35:11 +01:00
end
2014-04-13 09:25:17 +02:00
vel.speed = speed
crunch_tube(pos, cnode, cmeta)
2013-12-15 11:35:11 +01:00
if minetest.registered_nodes[cnode.name] and minetest.registered_nodes[cnode.name].tube and minetest.registered_nodes[cnode.name].tube.can_go then
2013-12-21 10:16:58 +01:00
can_go = minetest.registered_nodes[cnode.name].tube.can_go(pos, cnode, vel, stack)
2013-12-15 11:35:11 +01:00
else
2013-12-21 10:16:58 +01:00
can_go = pipeworks.notvel(adjlist, vel)
2013-12-15 11:35:11 +01:00
end
2014-08-14 16:22:03 +02:00
for _, vect in ipairs(can_go) do
local npos = vector.add(pos, vect)
pipeworks.load_position(npos)
2013-12-21 08:46:55 +01:00
local node = minetest.get_node(npos)
2014-08-24 17:51:43 +02:00
local reg_node = minetest.registered_nodes[node.name]
if reg_node then
local tube_def = reg_node.tube
local tubedevice = minetest.get_item_group(node.name, "tubedevice")
local tube_priority = (tube_def and tube_def.priority) or 100
if tubedevice > 0 and tube_priority >= max_priority then
if not tube_def or not tube_def.can_insert or
tube_def.can_insert(npos, node, stack, vect, owner) then
2014-08-24 17:51:43 +02:00
if tube_priority > max_priority then
max_priority = tube_priority
next_positions = {}
end
next_positions[#next_positions + 1] = {pos = npos, vect = vect}
2014-08-14 16:22:03 +02:00
end
2013-12-15 11:35:11 +01:00
end
end
end
2014-08-14 16:22:03 +02:00
if not next_positions[1] then
return false, nil
end
2014-08-14 16:22:03 +02:00
local n = (cmeta:get_int("tubedir") % (#next_positions)) + 1
if pipeworks.enable_cyclic_mode then
cmeta:set_int("tubedir", n)
2013-12-15 11:35:11 +01:00
end
2014-08-14 16:22:03 +02:00
local new_velocity = vector.multiply(next_positions[n].vect, vel.speed)
return true, new_velocity
2013-12-15 11:35:11 +01:00
end
minetest.register_entity("pipeworks:tubed_item", {
initial_properties = {
hp_max = 1,
physical = false,
collisionbox = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1},
visual = "wielditem",
visual_size = {x = 0.15, y = 0.15},
textures = {""},
spritediv = {x = 1, y = 1},
initial_sprite_basepos = {x = 0, y = 0},
is_visible = false,
},
2014-08-14 16:22:03 +02:00
physical_state = false,
2014-08-14 16:22:03 +02:00
from_data = function(self, itemstring)
local stack = ItemStack(itemstring)
2014-08-14 16:22:03 +02:00
local itemtable = stack:to_table()
local itemname = nil
if itemtable then
itemname = stack:to_table().name
end
local item_texture = nil
local item_type = ""
if minetest.registered_items[itemname] then
item_texture = minetest.registered_items[itemname].inventory_image
item_type = minetest.registered_items[itemname].type
end
self.object:set_properties({
is_visible = true,
2014-08-14 16:22:03 +02:00
textures = {stack:get_name()}
})
local def = stack:get_definition()
self.object:setyaw((def and def.type == "node") and 0 or math.pi * 0.25)
end,
2014-08-14 16:22:03 +02:00
get_staticdata = luaentity.get_staticdata,
2014-08-14 21:18:48 +02:00
on_activate = function(self, staticdata) -- Legacy code, should be replaced later by luaentity.on_activate
if staticdata == "" or staticdata == nil then
return
end
if staticdata == "toremove" then
self.object:remove()
return
end
local item = minetest.deserialize(staticdata)
pipeworks.tube_inject_item(self.object:getpos(), item.start_pos, item.velocity, item.itemstring)
2014-08-14 21:18:48 +02:00
self.object:remove()
end,
2014-08-14 16:22:03 +02:00
})
minetest.register_entity("pipeworks:color_entity", {
initial_properties = {
hp_max = 1,
physical = false,
collisionbox = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1},
visual = "cube",
visual_size = {x = 3.5, y = 3.5, z = 3.5}, -- todo: find correct size
textures = {""},
is_visible = false,
},
physical_state = false,
from_data = function(self, color)
local t = "pipeworks_color_"..color..".png"
local prop = {
is_visible = true,
visual = "cube",
textures = {t, t, t, t, t, t} -- todo: textures
}
self.object:set_properties(prop)
end,
2014-08-14 16:22:03 +02:00
get_staticdata = luaentity.get_staticdata,
on_activate = luaentity.on_activate,
})
-- see below for usage:
-- determine if go_next returned a multi-mode set.
local is_multimode = function(v)
return (type(v) == "table") and (v.__multimode)
end
2014-08-14 16:22:03 +02:00
luaentity.register_entity("pipeworks:tubed_item", {
itemstring = '',
item_entity = nil,
color_entity = nil,
color = nil,
start_pos = nil,
set_item = function(self, item)
local itemstring = ItemStack(item):to_string() -- Accept any input format
if self.itemstring == itemstring then
return
end
2014-08-14 16:22:03 +02:00
if self.item_entity then
self:remove_attached_entity(self.item_entity)
end
2014-08-14 16:22:03 +02:00
self.itemstring = itemstring
self.item_entity = self:add_attached_entity("pipeworks:tubed_item", itemstring)
end,
2014-08-14 16:22:03 +02:00
set_color = function(self, color)
if self.color == color then
return
end
self.color = color
if self.color_entity then
self:remove_attached_entity(self.color_entity)
end
if color then
self.color_entity = self:add_attached_entity("pipeworks:color_entity", color)
else
self.color_entity = nil
end
end,
on_step = function(self, dtime)
local pos = self:getpos()
if self.start_pos == nil then
2014-08-14 16:22:03 +02:00
self.start_pos = vector.round(pos)
self:setpos(pos)
2014-04-13 09:25:17 +02:00
end
2014-04-13 09:25:17 +02:00
local stack = ItemStack(self.itemstring)
2014-08-14 16:22:03 +02:00
local velocity = self:getvelocity()
2014-04-13 09:25:17 +02:00
local moved = false
local speed = math.abs(velocity.x + velocity.y + velocity.z)
2014-08-14 16:22:03 +02:00
if speed == 0 then
speed = 1
moved = true
end
local vel = {x = velocity.x / speed, y = velocity.y / speed, z = velocity.z / speed, speed = speed}
local moved_by = vector.distance(pos, self.start_pos)
if moved_by >= 1 then
2014-08-14 16:22:03 +02:00
self.start_pos = vector.add(self.start_pos, vel)
moved = true
end
pipeworks.load_position(self.start_pos)
2014-08-14 16:22:03 +02:00
local node = minetest.get_node(self.start_pos)
2014-04-13 09:25:17 +02:00
if moved and minetest.get_item_group(node.name, "tubedevice_receiver") == 1 then
2014-08-14 16:22:03 +02:00
local leftover
2014-04-13 09:25:17 +02:00
if minetest.registered_nodes[node.name].tube and minetest.registered_nodes[node.name].tube.insert_object then
leftover = minetest.registered_nodes[node.name].tube.insert_object(self.start_pos, node, stack, vel, self.owner)
2014-04-13 09:25:17 +02:00
else
leftover = stack
end
if leftover:is_empty() then
self:remove()
2014-04-13 09:25:17 +02:00
return
end
2014-08-14 16:22:03 +02:00
velocity = vector.multiply(velocity, -1)
self:setpos(vector.subtract(self.start_pos, vector.multiply(vel, moved_by - 1)))
2014-08-14 16:22:03 +02:00
self:setvelocity(velocity)
2014-04-13 09:25:17 +02:00
self:set_item(leftover:to_string())
return
end
2014-04-13 09:25:17 +02:00
if moved then
local found_next, new_velocity, multimode = go_next(self.start_pos, velocity, stack, self.owner) -- todo: color
local rev_vel = vector.multiply(velocity, -1)
local rev_dir = vector.direction(self.start_pos,vector.add(self.start_pos,rev_vel))
local rev_node = minetest.get_node(vector.round(vector.add(self.start_pos,rev_dir)))
local tube_present = minetest.get_item_group(rev_node.name,"tubedevice") == 1
2014-08-14 16:22:03 +02:00
if not found_next then
if pipeworks.drop_on_routing_fail or not tube_present or
minetest.get_item_group(rev_node.name,"tube") ~= 1 then
-- Using add_item instead of item_drop since this makes pipeworks backward
-- compatible with Minetest 0.4.13.
-- Using item_drop here makes Minetest 0.4.13 crash.
local dropped_item = minetest.add_item(self.start_pos, stack)
dropped_item:setvelocity(vector.multiply(velocity, 5))
self:remove()
return
else
velocity = vector.multiply(velocity, -1)
self:setpos(vector.subtract(self.start_pos, vector.multiply(vel, moved_by - 1)))
self:setvelocity(velocity)
2014-04-13 09:25:17 +02:00
end
elseif is_multimode(multimode) then
-- create new stacks according to returned data.
local s = self.start_pos
for _, split in ipairs(multimode) do
pipeworks.tube_inject_item(s, s, split.velocity, split.itemstack, self.owner)
end
-- remove ourself now the splits are sent
self:remove()
return
end
2014-08-14 16:22:03 +02:00
if new_velocity and not vector.equals(velocity, new_velocity) then
local nvelr = math.abs(new_velocity.x + new_velocity.y + new_velocity.z)
self:setpos(vector.add(self.start_pos, vector.multiply(new_velocity, (moved_by - 1) / nvelr)))
2014-08-14 16:22:03 +02:00
self:setvelocity(new_velocity)
end
2014-04-13 09:25:17 +02:00
end
end
})
2014-08-14 16:22:03 +02:00
if minetest.get_modpath("mesecons_mvps") then
mesecon.register_mvps_unmov("pipeworks:tubed_item")
mesecon.register_mvps_unmov("pipeworks:color_entity")
mesecon.register_on_mvps_move(function(moved_nodes)
2014-08-14 16:22:03 +02:00
local moved = {}
2014-01-03 13:29:38 +01:00
for _, n in ipairs(moved_nodes) do
2014-08-14 16:22:03 +02:00
moved[minetest.hash_node_position(n.oldpos)] = vector.subtract(n.pos, n.oldpos)
2014-01-03 13:29:38 +01:00
end
2014-08-14 16:22:03 +02:00
for id, entity in pairs(luaentity.entities) do
if entity.name == "pipeworks:tubed_item" then
local pos = entity:getpos()
local rpos = vector.round(pos)
local dir = moved[minetest.hash_node_position(rpos)]
if dir then
entity:setpos(vector.add(pos, dir))
entity.start_pos = vector.add(entity.start_pos, dir)
end
2014-01-03 13:29:38 +01:00
end
end
end)
end