pipeworks/item_transport.lua

493 lines
15 KiB
Lua
Raw Normal View History

dofile(pipeworks.modpath.."/compat.lua")
--and an extra function for getting the right-facing vector
local function facedir_to_right_dir(facedir)
--find the other directions
local backdir = minetest.facedir_to_dir(facedir)
local topdir = ({[0]={x=0, y=1, z=0},
{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=0, y=-1, z=0}})[math.floor(facedir/4)]
--return a cross product
return {x=topdir.y*backdir.z - backdir.y*topdir.z,
y=topdir.z*backdir.x - backdir.z*topdir.x,
z=topdir.x*backdir.y - backdir.x*topdir.y}
end
minetest.register_craftitem("pipeworks:filter", {
description = "Filter",
stack_max = 99,
})
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
local fakePlayer = {
get_player_name = function() return ":pipeworks" end,
-- any other player functions called by allow_metadata_inventory_take anywhere...
-- perhaps a custom metaclass that errors specially when fakePlayer.<property> is not found?
}
function pipeworks.tube_item(pos, item)
2013-12-15 11:35:11 +01:00
-- Take item in any format
local stack = ItemStack(item)
local obj = minetest.add_entity(pos, "pipeworks:tubed_item")
obj:get_luaentity():set_item(stack:to_string())
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 true if an item can be removed from that stack on that node
-- 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
-- sname = the current name to allow for, or nil if it allows anything
2013-12-15 11:35:11 +01:00
local function grabAndFire(frominv,frominvname,frompos,fromnode,sname,tube,idef,dir,all)
2013-12-21 10:16:58 +01:00
for spos,stack in ipairs(frominv:get_list(frominvname)) do
if (sname == nil and stack:get_name() ~= "") or stack:get_name() == sname then
local doRemove = true
if tube.can_remove then
doRemove = tube.can_remove(frompos, fromnode, stack, dir)
elseif idef.allow_metadata_inventory_take then
doRemove = idef.allow_metadata_inventory_take(frompos,"main",spos, stack, fakePlayer)
end
-- stupid lack of continue statements grumble
if doRemove then
local item
local count
if all then
count = stack:get_count()
else
count = 1
end
if tube.remove_items then
-- it could be the entire stack...
item = tube.remove_items(frompos, fromnode, stack, dir, count)
else
item = stack:take_item(count)
frominv:set_stack(frominvname, spos, stack)
if idef.on_metadata_inventory_take then
idef.on_metadata_inventory_take(frompos, "main", spos, item, fakePlayer)
end
end
local item1 = pipeworks.tube_item(vector.add(frompos, vector.multiply(dir, 1.4)), item)
item1:get_luaentity().start_pos = vector.add(frompos, dir)
2013-12-21 10:16:58 +01:00
item1:setvelocity(dir)
item1:setacceleration({x=0, y=0, z=0})
return true-- only fire one item, please
2013-12-21 10:16:58 +01:00
end
end
end
return false
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
end
minetest.register_node("pipeworks:filter", {
2013-01-28 18:41:14 +01:00
description = "Filter",
tiles = {"pipeworks_filter_top.png", "pipeworks_filter_top.png", "pipeworks_filter_output.png",
"pipeworks_filter_input.png", "pipeworks_filter_side.png", "pipeworks_filter_top.png"},
paramtype2 = "facedir",
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec",
"invsize[8,6.5;]"..
"list[current_name;main;0,0;8,2;]"..
"list[current_player;main;0,2.5;8,4;]")
meta:set_string("infotext", "Filter")
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
after_place_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
end,
after_dig_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
end,
mesecons={effector={action_on=function(pos,node)
minetest.registered_nodes[node.name].on_punch(pos,node,nil)
end}},
tube={connect_sides={right=1}},
on_punch = function (pos, node, puncher)
2013-12-21 10:16:58 +01:00
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
local dir = facedir_to_right_dir(node.param2)
local frompos = {x=pos.x - dir.x, y=pos.y - dir.y, z=pos.z - dir.z}
local fromnode=minetest.get_node(frompos)
if not fromnode then return end
local idef = minetest.registered_nodes[fromnode.name]
-- assert(idef)
local tube = idef.tube
if not (tube and tube.input_inventory) then
return
end
2013-12-21 10:16:58 +01:00
local frommeta = minetest.get_meta(frompos)
local frominvname = tube.input_inventory
local frominv = frommeta:get_inventory()
local sname
for _,filter in ipairs(inv:get_list("main")) do
sname = filter:get_name()
if sname ~= "" then
-- XXX: that's a lot of parameters
if grabAndFire(frominv, frominvname, frompos, fromnode, sname, tube, idef, dir) then return end
2013-12-21 10:16:58 +01:00
end
end
if inv:is_empty("main") then
grabAndFire(frominv,frominvname,frompos,fromnode,nil,tube,idef,dir)
end
2013-12-21 10:16:58 +01:00
end,
})
2013-01-28 18:41:14 +01:00
minetest.register_craftitem("pipeworks:mese_filter", {
description = "Mese filter",
stack_max = 99,
})
minetest.register_node("pipeworks:mese_filter", {
description = "Mese filter",
tiles = {"pipeworks_mese_filter_top.png", "pipeworks_mese_filter_top.png", "pipeworks_mese_filter_output.png",
"pipeworks_mese_filter_input.png", "pipeworks_mese_filter_side.png", "pipeworks_mese_filter_top.png"},
paramtype2 = "facedir",
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
2013-01-28 18:41:14 +01:00
meta:set_string("formspec",
"invsize[8,6.5;]"..
"list[current_name;main;0,0;8,2;]"..
"list[current_player;main;0,2.5;8,4;]")
2013-01-28 18:41:14 +01:00
meta:set_string("infotext", "Mese filter")
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
2013-01-28 18:41:14 +01:00
local inv = meta:get_inventory()
return inv:is_empty("main")
end,
after_place_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
end,
after_dig_node = function(pos)
pipeworks.scan_for_tube_objects(pos)
end,
2013-01-28 18:41:14 +01:00
mesecons={effector={action_on=function(pos,node)
minetest.registered_nodes[node.name].on_punch(pos,node,nil)
end}},
tube={connect_sides={right=1}},
2013-01-28 18:41:14 +01:00
on_punch = function (pos, node, puncher)
2013-12-21 10:16:58 +01:00
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
local dir = facedir_to_right_dir(node.param2)
local frompos = {x=pos.x - dir.x, y=pos.y - dir.y, z=pos.z - dir.z}
local fromnode=minetest.get_node(frompos)
local idef = minetest.registered_nodes[fromnode.name]
-- assert(idef)
local tube = idef.tube
if not (tube and tube.input_inventory) then
return
2013-01-28 18:41:14 +01:00
end
2013-12-21 10:16:58 +01:00
local frommeta = minetest.get_meta(frompos)
local frominvname = minetest.registered_nodes[fromnode.name].tube.input_inventory
local frominv = frommeta:get_inventory()
local sname
for _,filter in ipairs(inv:get_list("main")) do
sname = filter:get_name()
if sname ~= "" then
if grabAndFire(frominv, frominvname, frompos, fromnode, sname, tube, idef, dir, true) then return end
2013-12-21 10:16:58 +01:00
end
end
if inv:is_empty("main") then
grabAndFire(frominv, frominvname, frompos, fromnode, nil, tube, idef, dir, true)
2013-12-21 10:16:58 +01:00
end
end,
2013-01-28 18:41:14 +01:00
})
2013-06-05 15:54:03 +02:00
local function roundpos(pos)
return {x=math.floor(pos.x+0.5),y=math.floor(pos.y+0.5),z=math.floor(pos.z+0.5)}
end
2013-12-15 11:35:11 +01:00
local function addVect(pos,vect)
return {x=pos.x+vect.x,y=pos.y+vect.y,z=pos.z+vect.z}
end
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
2013-12-21 10:16:58 +01:00
local function go_next(pos, velocity, stack)
2013-12-21 08:46:55 +01:00
local chests = {}
local tubes = {}
local cnode = minetest.get_node(pos)
local cmeta = minetest.get_meta(pos)
2013-12-15 11:35:11 +01:00
local n
local can_go
2013-12-21 10:16:58 +01:00
local speed = math.abs(velocity.x + velocity.y + velocity.z)
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
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
vel.speed=speed
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
2013-12-15 12:10:03 +01:00
local meta = nil
2013-12-15 11:35:11 +01:00
for _,vect in ipairs(can_go) do
2013-12-21 08:46:55 +01:00
local npos = addVect(pos,vect)
local node = minetest.get_node(npos)
local tube_receiver = minetest.get_item_group(node.name,"tubedevice_receiver")
meta = minetest.get_meta(npos)
local tubelike = meta:get_int("tubelike")
if tube_receiver == 1 then
2013-12-15 11:35:11 +01:00
if minetest.registered_nodes[node.name].tube and
minetest.registered_nodes[node.name].tube.can_insert and
2013-12-21 10:16:58 +01:00
minetest.registered_nodes[node.name].tube.can_insert(npos, node, stack, vect) then
2013-12-21 08:46:55 +01:00
local i = #chests + 1
2013-12-21 10:16:58 +01:00
chests[i] = {}
chests[i].pos = npos
chests[i].vect = vect
2013-12-15 11:35:11 +01:00
end
2013-12-21 08:46:55 +01:00
elseif tubelike == 1 then
local i = #tubes + 1
2013-12-21 10:16:58 +01:00
tubes[i] = {}
tubes[i].pos = npos
tubes[i].vect = vect
2013-12-15 11:35:11 +01:00
end
end
2013-12-21 08:46:55 +01:00
if chests[1] == nil then--no chests found
if tubes[1] == nil then
2013-12-15 11:35:11 +01:00
return 0
else
2013-12-21 10:16:58 +01:00
n = (cmeta:get_int("tubedir")%(#tubes)) + 1
2013-12-15 11:35:11 +01:00
if pipeworks.enable_cyclic_mode then
2013-12-21 10:16:58 +01:00
cmeta:set_int("tubedir",n)
2013-12-15 11:35:11 +01:00
end
2013-12-21 08:46:55 +01:00
velocity.x = tubes[n].vect.x*vel.speed
velocity.y = tubes[n].vect.y*vel.speed
velocity.z = tubes[n].vect.z*vel.speed
2013-12-15 11:35:11 +01:00
end
else
2013-12-21 10:16:58 +01:00
n = (cmeta:get_int("tubedir")%(#chests))+1
2013-12-15 11:35:11 +01:00
if pipeworks.enable_cyclic_mode then
2013-12-21 10:16:58 +01:00
cmeta:set_int("tubedir",n)
2013-12-15 11:35:11 +01:00
end
2013-12-21 08:46:55 +01:00
velocity.x = chests[n].vect.x*speed
velocity.y = chests[n].vect.y*speed
velocity.z = chests[n].vect.z*speed
2013-12-15 11:35:11 +01:00
end
return 1
end
minetest.register_entity("pipeworks:tubed_item", {
initial_properties = {
hp_max = 1,
physical = false,
-- collisionbox = {0,0,0,0,0,0},
collisionbox = {0.1,0.1,0.1,0.1,0.1,0.1},
visual = "sprite",
visual_size = {x=0.5, y=0.5},
textures = {""},
spritediv = {x=1, y=1},
initial_sprite_basepos = {x=0, y=0},
is_visible = false,
start_pos={},
route={}
},
itemstring = '',
physical_state = false,
set_item = function(self, itemstring)
self.itemstring = itemstring
local stack = ItemStack(itemstring)
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
prop = {
is_visible = true,
visual = "sprite",
textures = {"unknown_item.png"}
}
if item_texture and item_texture ~= "" then
prop.visual = "sprite"
prop.textures = {item_texture}
prop.visual_size = {x=0.3, y=0.3}
else
prop.visual = "wielditem"
prop.textures = {itemname}
prop.visual_size = {x=0.15, y=0.15}
end
self.object:set_properties(prop)
end,
get_staticdata = function(self)
2013-01-19 13:14:41 +01:00
if self.start_pos==nil then return end
local velocity=self.object:getvelocity()
--self.object:setvelocity({x=0,y=0,z=0})
self.object:setpos(self.start_pos)
return minetest.serialize({
itemstring=self.itemstring,
2013-01-19 13:14:41 +01:00
velocity=velocity,
start_pos=self.start_pos
})
end,
on_activate = function(self, staticdata)
if staticdata=="" or staticdata==nil then return end
local item = minetest.deserialize(staticdata)
local stack = ItemStack(item.itemstring)
local itemtable = stack:to_table()
local itemname = nil
if itemtable then
itemname = stack:to_table().name
end
if itemname then
self.start_pos=item.start_pos
self.object:setvelocity(item.velocity)
self.object:setacceleration({x=0, y=0, z=0})
self.object:setpos(item.start_pos)
end
self:set_item(item.itemstring)
end,
on_step = function(self, dtime)
2013-06-05 15:54:03 +02:00
if self.start_pos==nil then
local pos = self.object:getpos()
self.start_pos=roundpos(pos)
end
local pos = self.object:getpos()
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
tubelike=meta:get_int("tubelike")
local stack = ItemStack(self.itemstring)
local drop_pos=nil
local velocity=self.object:getvelocity()
if velocity==nil then return end
local velocitycopy={x=velocity.x,y=velocity.y,z=velocity.z}
local moved=false
2013-01-19 13:14:41 +01:00
local speed=math.abs(velocity.x+velocity.y+velocity.z)
2013-10-21 11:34:10 +02:00
local vel={x=velocity.x/speed,y=velocity.y/speed,z=velocity.z/speed, speed=speed}
2013-01-19 13:14:41 +01:00
if math.abs(vel.x)==1 then
local next_node=math.abs(pos.x-self.start_pos.x)
if next_node >= 1 then
2013-01-19 13:14:41 +01:00
self.start_pos.x=self.start_pos.x+vel.x
moved=true
end
2013-01-19 13:14:41 +01:00
elseif math.abs(vel.y)==1 then
local next_node=math.abs(pos.y-self.start_pos.y)
if next_node >= 1 then
2013-01-19 13:14:41 +01:00
self.start_pos.y=self.start_pos.y+vel.y
moved=true
end
2013-01-19 13:14:41 +01:00
elseif math.abs(vel.z)==1 then
local next_node=math.abs(pos.z-self.start_pos.z)
if next_node >= 1 then
2013-01-19 13:14:41 +01:00
self.start_pos.z=self.start_pos.z+vel.z
moved=true
end
end
local sposcopy={x=self.start_pos.x,y=self.start_pos.y,z=self.start_pos.z}
node = minetest.get_node(self.start_pos)
if moved and minetest.get_item_group(node.name,"tubedevice_receiver")==1 then
2013-12-15 12:10:03 +01:00
local leftover = nil
if minetest.registered_nodes[node.name].tube and minetest.registered_nodes[node.name].tube.insert_object then
2013-01-19 13:14:41 +01:00
leftover = minetest.registered_nodes[node.name].tube.insert_object(self.start_pos,node,stack,vel)
else
leftover = stack
end
--drop_pos=minetest.find_node_near(self.start_pos,1,"air")
--if drop_pos and not leftover:is_empty() then minetest.item_drop(leftover,"",drop_pos) end
--self.object:remove()
if leftover:is_empty() then
self.object:remove()
return
end
velocity.x=-velocity.x
velocity.y=-velocity.y
velocity.z=-velocity.z
self.object:setvelocity(velocity)
self:set_item(leftover:to_string())
return
end
if moved then
2013-12-21 08:46:55 +01:00
if go_next (self.start_pos, velocity, stack) == 0 then
drop_pos=minetest.find_node_near({x=self.start_pos.x+velocity.x,y=self.start_pos.y+velocity.y,z=self.start_pos.z+velocity.z}, 1, "air")
if drop_pos then
minetest.item_drop(stack, "", drop_pos)
self.object:remove()
end
end
end
if velocity.x~=velocitycopy.x or velocity.y~=velocitycopy.y or velocity.z~=velocitycopy.z or
self.start_pos.x~=sposcopy.x or self.start_pos.y~=sposcopy.y or self.start_pos.z~=sposcopy.z then
self.object:setpos(self.start_pos)
self.object:setvelocity(velocity)
end
end
})
2014-01-03 13:29:38 +01:00
if minetest.get_modpath("mesecons_mvps") ~= nil then
mesecon:register_mvps_unmov("pipeworks:tubed_item")
mesecon:register_on_mvps_move(function(moved_nodes)
local objects_to_move = {}
for _, n in ipairs(moved_nodes) do
local objects = minetest.get_objects_inside_radius(n.oldpos, 1)
for _, obj in ipairs(objects) do
local entity = obj:get_luaentity()
if entity and entity.name == "pipeworks:tubed_item" then
objects_to_move[#objects_to_move+1] = obj
end
end
end
if #objects_to_move > 0 then
local dir = vector.subtract(moved_nodes[1].pos, moved_nodes[1].oldpos)
for _, obj in ipairs(objects_to_move) do
local entity = obj:get_luaentity()
obj:setpos(vector.add(obj:getpos(), dir))
entity.start_pos = vector.add(entity.start_pos, dir)
end
end
end)
end