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)
@ -75,117 +137,90 @@ local function grabAndFire(frominv,frominvname,frompos,fromnode,sname,tube,idef,
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,
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", "Itemwise Filter-Injector")
local inv = meta:get_inventory()
inv:set_size("main", 8*2)
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)
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 if not fromnode then return end
local idef = minetest.registered_nodes[fromnode.name] local fromdef = minetest.registered_nodes[fromnode.name]
-- assert(idef) if not fromdef then return end
local tube = idef.tube local fromtube = fromdef.tube
if not (tube and tube.input_inventory) then if not (fromtube and fromtube.input_inventory) then return end
return local filters = {}
end for _, filterstack in ipairs(filtinv:get_list("main")) do
if tube.before_filter then local filtername = filterstack:get_name()
tube.before_filter(frompos) if filtername ~= "" then table.insert(filters, filtername) end
end end
if #filters == 0 then table.insert(filters, "") end
local slotseq_mode = filtmeta:get_int("slotseq_mode")
local frommeta = minetest.get_meta(frompos) local frommeta = minetest.get_meta(frompos)
local frominv = frommeta:get_inventory() local frominv = frommeta:get_inventory()
if fromtube.before_filter then fromtube.before_filter(frompos) end
local function from_inventory(frominvname) for _, frominvname in ipairs(type(fromtube.input_inventory) == "table" and fromtube.input_inventory or {fromtube.input_inventory}) do
local sname local done = false
for _,filter in ipairs(inv:get_list("main")) do for _, filtername in ipairs(filters) do
sname = filter:get_name() if grabAndFire(data, slotseq_mode, filtmeta, frominv, frominvname, frompos, fromnode, filtername, fromtube, fromdef, dir, data.stackwise) then
if sname ~= "" then done = true
-- XXX: that's a lot of parameters
if grabAndFire(frominv, frominvname, frompos, fromnode, sname, tube, idef, dir) then
return true
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
if type(tube.input_inventory) == "table" then
for _, i in ipairs(tube.input_inventory) do
if from_inventory(i) then -- fired an item
break break
end end
end end
else if done then break end
from_inventory(tube.input_inventory)
end end
if fromtube.after_filter then fromtube.after_filter(frompos) end
end
if tube.after_filter then for _, data in ipairs({
tube.after_filter(frompos) {
end name = "filter",
end, wise_desc = "Itemwise",
}) stackwise = false,
},
minetest.register_node("pipeworks:mese_filter", { {
description = "Stackwise Filter-Injector", name = "mese_filter",
tiles = {"pipeworks_mese_filter_top.png", "pipeworks_mese_filter_top.png", "pipeworks_mese_filter_output.png", wise_desc = "Stackwise",
"pipeworks_mese_filter_input.png", "pipeworks_mese_filter_side.png", "pipeworks_mese_filter_top.png"}, stackwise = true,
},
}) do
minetest.register_node("pipeworks:"..data.name, {
description = data.wise_desc.." Filter-Injector",
tiles = {
"pipeworks_"..data.name.."_top.png",
"pipeworks_"..data.name.."_top.png",
"pipeworks_"..data.name.."_output.png",
"pipeworks_"..data.name.."_input.png",
"pipeworks_"..data.name.."_side.png",
"pipeworks_"..data.name.."_top.png",
},
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2}, groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,tubedevice=1,mesecon=2},
legacy_facedir_simple = true, legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(), sounds = default.node_sound_wood_defaults(),
on_construct = function(pos) on_construct = function(pos)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
meta:set_string("formspec", set_filter_formspec(data, meta)
"invsize[8,6.5;]".. set_filter_infotext(data, meta)
"list[current_name;main;0,0;8,2;]"..
"list[current_player;main;0,2.5;8,4;]")
meta:set_string("infotext", "Stackwise Filter-Injector")
local inv = meta:get_inventory() local inv = meta:get_inventory()
inv:set_size("main", 8*2) inv:set_size("main", 8*2)
end, end,
on_receive_fields = function(pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
for k, _ in pairs(fields) do
if k:sub(1, 12) == "slotseq_mode" then
local mode = tonumber(k:sub(13, 13))
meta:set_int("slotseq_mode", mode)
meta:set_int("slotseq_index", mode == 2 and 1 or 0)
end
end
set_filter_formspec(data, meta)
set_filter_infotext(data, meta)
end,
can_dig = function(pos,player) can_dig = function(pos,player)
local meta = minetest.get_meta(pos); local meta = minetest.get_meta(pos)
local inv = meta:get_inventory() local inv = meta:get_inventory()
return inv:is_empty("main") return inv:is_empty("main")
end, end,
@ -195,63 +230,19 @@ minetest.register_node("pipeworks:mese_filter", {
after_dig_node = function(pos) after_dig_node = function(pos)
pipeworks.scan_for_tube_objects(pos) pipeworks.scan_for_tube_objects(pos)
end, end,
mesecons={effector={action_on=function(pos,node) mesecons = {
minetest.registered_nodes[node.name].on_punch(pos,node,nil) effector = {
end}}, action_on = function(pos, node)
punch_filter(data, pos, node)
end,
},
},
tube={connect_sides={right=1}}, tube={connect_sides={right=1}},
on_punch = function (pos, node, puncher) on_punch = function (pos, node, puncher)
local meta = minetest.get_meta(pos); punch_filter(data, pos, node)
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
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
if inv:is_empty("main") then
if grabAndFire(frominv, frominvname, frompos, fromnode, nil, tube, idef, dir, true) then
return true
end
end
return false
end
if type(tube.input_inventory) == "table" then
for _, i in ipairs(tube.input_inventory) do
if from_inventory(i) then -- fired an item
break
end
end
else
from_inventory(tube.input_inventory)
end
if tube.after_filter then
tube.after_filter(frompos)
end
end, 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)}