Slot sequencing modes for filter-injectors

The filter-injectors used to always prefer to take items from the
beginning of the source inventory.  Because items also tend to get placed
at the beginning of an inventory, this could lead to a chest+injector
buffer only passing through a trickle of one type of item, where other
item types are flowing in and getting later inventory slots.  So now,
the sequencing of source inventory slots is configurable, per-injector,
by form.  In addition to the old priority mode, there's now a random
mode and a rotation mode.

Incidentally refactor the filter-injector code, to avoid duplication
between the itemwise and stackwise variants.
This commit is contained in:
Zefram 2014-07-21 23:55:38 +01:00 committed by Vanessa Ezekowitz
parent 1c3fb6b1c5
commit b832c0ad06

View File

@ -38,19 +38,81 @@ end
-- both optional w/ sensible defaults and fallback to normal allow_* function -- both optional w/ sensible defaults and fallback to normal allow_* function
-- XXX: possibly change insert_object to insert_item -- XXX: possibly change insert_object to insert_item
-- sname = the current name to allow for, or nil if it allows anything local function set_filter_infotext(data, meta)
local infotext = data.wise_desc.." Filter-Injector"
if meta:get_int("slotseq_mode") == 2 then
infotext = infotext .. " (slot #"..meta:get_int("slotseq_index").." next)"
end
meta:set_string("infotext", infotext)
end
local function grabAndFire(frominv,frominvname,frompos,fromnode,sname,tube,idef,dir,all) local function set_filter_formspec(data, meta)
local itemname = data.wise_desc.." Filter-Injector"
local formspec = "size[8,8.5]"..
"item_image[0,0;1,1;pipeworks:"..data.name.."]"..
"label[1,0;"..minetest.formspec_escape(itemname).."]"..
"label[0,1;Prefer item types:]"..
"list[current_name;main;0,1.5;8,2;]"
local slotseq_mode = meta:get_int("slotseq_mode")
if slotseq_mode == 1 then
formspec = formspec .. "button[0,3.5;4,1;slotseq_mode2;Sequence slots Randomly]"
elseif slotseq_mode == 2 then
formspec = formspec .. "button[0,3.5;4,1;slotseq_mode0;Sequence slots by Rotation]"
else
formspec = formspec .. "button[0,3.5;4,1;slotseq_mode1;Sequence slots by Priority]"
end
formspec = formspec .. "list[current_player;main;0,4.5;8,4;]"
meta:set_string("formspec", formspec)
end
local function grabAndFire(data,slotseq_mode,filtmeta,frominv,frominvname,frompos,fromnode,filtername,fromtube,fromdef,dir,all)
local sposes = {}
for spos,stack in ipairs(frominv:get_list(frominvname)) do for spos,stack in ipairs(frominv:get_list(frominvname)) do
if (sname == nil and stack:get_name() ~= "") or stack:get_name() == sname then local matches
if filtername == "" then
matches = stack:get_name() ~= ""
else
matches = stack:get_name() == filtername
end
if matches then table.insert(sposes, spos) end
end
if #sposes == 0 then return false end
if slotseq_mode == 1 then
for i = #sposes, 2, -1 do
local j = math.random(i)
local t = sposes[j]
sposes[j] = sposes[i]
sposes[i] = t
end
elseif slotseq_mode == 2 then
local headpos = filtmeta:get_int("slotseq_index")
table.sort(sposes, function (a, b)
if a >= headpos then
if b < headpos then return true end
else
if b >= headpos then return false end
end
return a < b
end)
end
for _, spos in ipairs(sposes) do
local stack = frominv:get_stack(frominvname, spos)
local doRemove = stack:get_count() local doRemove = stack:get_count()
if tube.can_remove then if fromtube.can_remove then
doRemove = tube.can_remove(frompos, fromnode, stack, dir) doRemove = fromtube.can_remove(frompos, fromnode, stack, dir)
elseif idef.allow_metadata_inventory_take then elseif fromdef.allow_metadata_inventory_take then
doRemove = idef.allow_metadata_inventory_take(frompos, frominvname,spos, stack, fakePlayer) doRemove = fromdef.allow_metadata_inventory_take(frompos, frominvname,spos, stack, fakePlayer)
end end
-- stupid lack of continue statements grumble -- stupid lack of continue statements grumble
if doRemove > 0 then if doRemove > 0 then
if slotseq_mode == 2 then
local nextpos = spos + 1
if nextpos > frominv:get_size(frominvname) then
nextpos = 1
end
filtmeta:set_int("slotseq_index", nextpos)
set_filter_infotext(data, filtmeta)
end
local item local item
local count local count
if all then if all then
@ -58,14 +120,14 @@ local function grabAndFire(frominv,frominvname,frompos,fromnode,sname,tube,idef,
else else
count = 1 count = 1
end end
if tube.remove_items then if fromtube.remove_items then
-- it could be the entire stack... -- it could be the entire stack...
item = tube.remove_items(frompos, fromnode, stack, dir, count) item = fromtube.remove_items(frompos, fromnode, stack, dir, count)
else else
item = stack:take_item(count) item = stack:take_item(count)
frominv:set_stack(frominvname, spos, stack) frominv:set_stack(frominvname, spos, stack)
if idef.on_metadata_inventory_take then if fromdef.on_metadata_inventory_take then
idef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakePlayer) fromdef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakePlayer)
end end
end end
local item1 = pipeworks.tube_item(vector.add(frompos, vector.multiply(dir, 1.4)), item) local item1 = pipeworks.tube_item(vector.add(frompos, vector.multiply(dir, 1.4)), item)
@ -74,184 +136,113 @@ local function grabAndFire(frominv,frominvname,frompos,fromnode,sname,tube,idef,
item1:setacceleration({x=0, y=0, z=0}) item1:setacceleration({x=0, y=0, z=0})
return true-- only fire one item, please return true-- only fire one item, please
end end
end
end end
return false return false
end end
minetest.register_node("pipeworks:filter", { local function punch_filter(data, filtpos, filtnode)
description = "Itemwise Filter-Injector", local filtmeta = minetest.get_meta(filtpos)
tiles = {"pipeworks_filter_top.png", "pipeworks_filter_top.png", "pipeworks_filter_output.png", local filtinv = filtmeta:get_inventory()
"pipeworks_filter_input.png", "pipeworks_filter_side.png", "pipeworks_filter_top.png"}, local dir = facedir_to_right_dir(filtnode.param2)
paramtype2 = "facedir", local frompos = {x=filtpos.x - dir.x, y=filtpos.y - dir.y, z=filtpos.z - dir.z}
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2}, local fromnode = minetest.get_node(frompos)
legacy_facedir_simple = true, if not fromnode then return end
sounds = default.node_sound_wood_defaults(), local fromdef = minetest.registered_nodes[fromnode.name]
on_construct = function(pos) if not fromdef then return end
local meta = minetest.get_meta(pos) local fromtube = fromdef.tube
meta:set_string("formspec", if not (fromtube and fromtube.input_inventory) then return end
"invsize[8,6.5;]".. local filters = {}
"list[current_name;main;0,0;8,2;]".. for _, filterstack in ipairs(filtinv:get_list("main")) do
"list[current_player;main;0,2.5;8,4;]") local filtername = filterstack:get_name()
meta:set_string("infotext", "Itemwise Filter-Injector") if filtername ~= "" then table.insert(filters, filtername) end
local inv = meta:get_inventory() end
inv:set_size("main", 8*2) if #filters == 0 then table.insert(filters, "") end
end, local slotseq_mode = filtmeta:get_int("slotseq_mode")
can_dig = function(pos,player) local frommeta = minetest.get_meta(frompos)
local meta = minetest.get_meta(pos); local frominv = frommeta:get_inventory()
local inv = meta:get_inventory() if fromtube.before_filter then fromtube.before_filter(frompos) end
return inv:is_empty("main") for _, frominvname in ipairs(type(fromtube.input_inventory) == "table" and fromtube.input_inventory or {fromtube.input_inventory}) do
end, local done = false
after_place_node = function(pos) for _, filtername in ipairs(filters) do
pipeworks.scan_for_tube_objects(pos) if grabAndFire(data, slotseq_mode, filtmeta, frominv, frominvname, frompos, fromnode, filtername, fromtube, fromdef, dir, data.stackwise) then
end, done = true
after_dig_node = function(pos) break
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)
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
if tube.before_filter then
tube.before_filter(frompos)
end
local frommeta = minetest.get_meta(frompos)
local frominv = frommeta:get_inventory()
local function from_inventory(frominvname)
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 true
end
end
end end
if inv:is_empty("main") then
if grabAndFire(frominv, frominvname, frompos, fromnode, nil, tube, idef, dir) then
return true
end
end
return false
end end
if done then break end
if type(tube.input_inventory) == "table" then end
for _, i in ipairs(tube.input_inventory) do if fromtube.after_filter then fromtube.after_filter(frompos) end
if from_inventory(i) then -- fired an item end
break
end
end
else
from_inventory(tube.input_inventory)
end
if tube.after_filter then
tube.after_filter(frompos)
end
end,
})
minetest.register_node("pipeworks:mese_filter", { for _, data in ipairs({
description = "Stackwise Filter-Injector", {
tiles = {"pipeworks_mese_filter_top.png", "pipeworks_mese_filter_top.png", "pipeworks_mese_filter_output.png", name = "filter",
"pipeworks_mese_filter_input.png", "pipeworks_mese_filter_side.png", "pipeworks_mese_filter_top.png"}, wise_desc = "Itemwise",
paramtype2 = "facedir", stackwise = false,
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2}, },
legacy_facedir_simple = true, {
sounds = default.node_sound_wood_defaults(), name = "mese_filter",
on_construct = function(pos) wise_desc = "Stackwise",
local meta = minetest.get_meta(pos) stackwise = true,
meta:set_string("formspec", },
"invsize[8,6.5;]".. }) do
"list[current_name;main;0,0;8,2;]".. minetest.register_node("pipeworks:"..data.name, {
"list[current_player;main;0,2.5;8,4;]") description = data.wise_desc.." Filter-Injector",
meta:set_string("infotext", "Stackwise Filter-Injector") tiles = {
local inv = meta:get_inventory() "pipeworks_"..data.name.."_top.png",
inv:set_size("main", 8*2) "pipeworks_"..data.name.."_top.png",
end, "pipeworks_"..data.name.."_output.png",
can_dig = function(pos,player) "pipeworks_"..data.name.."_input.png",
local meta = minetest.get_meta(pos); "pipeworks_"..data.name.."_side.png",
local inv = meta:get_inventory() "pipeworks_"..data.name.."_top.png",
return inv:is_empty("main") },
end, paramtype2 = "facedir",
after_place_node = function(pos) groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2},
pipeworks.scan_for_tube_objects(pos) legacy_facedir_simple = true,
end, sounds = default.node_sound_wood_defaults(),
after_dig_node = function(pos) on_construct = function(pos)
pipeworks.scan_for_tube_objects(pos) local meta = minetest.get_meta(pos)
end, set_filter_formspec(data, meta)
mesecons={effector={action_on=function(pos,node) set_filter_infotext(data, meta)
minetest.registered_nodes[node.name].on_punch(pos,node,nil) local inv = meta:get_inventory()
end}}, inv:set_size("main", 8*2)
tube={connect_sides={right=1}}, end,
on_punch = function (pos, node, puncher) on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos); local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() for k, _ in pairs(fields) do
local dir = facedir_to_right_dir(node.param2) if k:sub(1, 12) == "slotseq_mode" then
local frompos = {x=pos.x - dir.x, y=pos.y - dir.y, z=pos.z - dir.z} local mode = tonumber(k:sub(13, 13))
local fromnode=minetest.get_node(frompos) meta:set_int("slotseq_mode", mode)
local idef = minetest.registered_nodes[fromnode.name] meta:set_int("slotseq_index", mode == 2 and 1 or 0)
-- assert(idef)
local tube = idef.tube
if not (tube and tube.input_inventory) then
return
end
if tube.before_filter then
tube.before_filter(frompos)
end
local frommeta = minetest.get_meta(frompos)
local frominv = frommeta:get_inventory()
local function from_inventory(frominvname)
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, true) then
return true
end
end end
end end
if inv:is_empty("main") then set_filter_formspec(data, meta)
if grabAndFire(frominv, frominvname, frompos, fromnode, nil, tube, idef, dir, true) then set_filter_infotext(data, meta)
return true end,
end can_dig = function(pos,player)
end local meta = minetest.get_meta(pos)
return false local inv = meta:get_inventory()
end return inv:is_empty("main")
end,
if type(tube.input_inventory) == "table" then after_place_node = function(pos)
for _, i in ipairs(tube.input_inventory) do pipeworks.scan_for_tube_objects(pos)
if from_inventory(i) then -- fired an item end,
break after_dig_node = function(pos)
end pipeworks.scan_for_tube_objects(pos)
end end,
else mesecons = {
from_inventory(tube.input_inventory) effector = {
end action_on = function(pos, node)
punch_filter(data, pos, node)
if tube.after_filter then end,
tube.after_filter(frompos) },
end },
end, tube={connect_sides={right=1}},
}) on_punch = function (pos, node, puncher)
punch_filter(data, pos, node)
end,
})
end
local function roundpos(pos) 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)} return {x=math.floor(pos.x+0.5),y=math.floor(pos.y+0.5),z=math.floor(pos.z+0.5)}