Merge branch 'filter-injector-refactor' into 'master'

filter-injector.lua: eliminate most parameters from grabAndFire by making it a closure

See merge request VanessaE/pipeworks!13
This commit is contained in:
Vanessa Dannenberg 2020-01-10 19:48:57 +00:00
commit 0fa88b7054

View File

@ -53,116 +53,6 @@ local function set_filter_formspec(data, meta)
meta:set_string("formspec", formspec) meta:set_string("formspec", formspec)
end end
-- todo SOON: this function has *way too many* parameters
local function grabAndFire(data,slotseq_mode,exmatch_mode,filtmeta,frominv,frominvname,frompos,fromnode,filterfor,fromtube,fromdef,dir,fakePlayer,all,digiline)
local sposes = {}
if not frominvname or not frominv:get_list(frominvname) then return end
for spos,stack in ipairs(frominv:get_list(frominvname)) do
local matches
if filterfor == "" then
matches = stack:get_name() ~= ""
else
local fname = filterfor.name
local fgroup = filterfor.group
local fwear = filterfor.wear
local fmetadata = filterfor.metadata
matches = (not fname -- If there's a name filter,
or stack:get_name() == fname) -- it must match.
and (not fgroup -- If there's a group filter,
or (type(fgroup) == "string" -- it must be a string
and minetest.get_item_group( -- and it must match.
stack:get_name(), fgroup) ~= 0))
and (not fwear -- If there's a wear filter:
or (type(fwear) == "number" -- If it's a number,
and stack:get_wear() == fwear) -- it must match.
or (type(fwear) == "table" -- If it's a table:
and (not fwear[1] -- If there's a lower bound,
or (type(fwear[1]) == "number" -- it must be a number
and fwear[1] <= stack:get_wear())) -- and it must be <= the actual wear.
and (not fwear[2] -- If there's an upper bound
or (type(fwear[2]) == "number" -- it must be a number
and stack:get_wear() < fwear[2])))) -- and it must be > the actual wear.
-- If the wear filter is of any other type, fail.
--
and (not fmetadata -- If there's a matadata filter,
or (type(fmetadata) == "string" -- it must be a string
and stack:get_metadata() == fmetadata)) -- and it must match.
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()
if fromtube.can_remove then
doRemove = fromtube.can_remove(frompos, fromnode, stack, dir, frominvname, spos)
elseif fromdef.allow_metadata_inventory_take then
doRemove = fromdef.allow_metadata_inventory_take(frompos, frominvname,spos, stack, fakePlayer)
end
-- stupid lack of continue statements grumble
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 count
if all then
count = math.min(stack:get_count(), doRemove)
if filterfor.count and (filterfor.count > 1 or digiline) then
if exmatch_mode ~= 0 and filterfor.count > count then
return false -- not enough, fail
else
-- limit quantity to filter amount
count = math.min(filterfor.count, count)
end
end
else
count = 1
end
if fromtube.remove_items then
-- it could be the entire stack...
item = fromtube.remove_items(frompos, fromnode, stack, dir, count, frominvname, spos)
else
item = stack:take_item(count)
frominv:set_stack(frominvname, spos, stack)
if fromdef.on_metadata_inventory_take then
fromdef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakePlayer)
end
end
local pos = vector.add(frompos, vector.multiply(dir, 1.4))
local start_pos = vector.add(frompos, dir)
local item1 = pipeworks.tube_inject_item(pos, start_pos, dir, item, fakePlayer:get_player_name())
return true-- only fire one item, please
end
end
return false
end
local function punch_filter(data, filtpos, filtnode, msg) local function punch_filter(data, filtpos, filtnode, msg)
local filtmeta = minetest.get_meta(filtpos) local filtmeta = minetest.get_meta(filtpos)
local filtinv = filtmeta:get_inventory() local filtinv = filtmeta:get_inventory()
@ -214,7 +104,7 @@ local function punch_filter(data, filtpos, filtnode, msg)
if not (fromtube and fromtube.input_inventory) then return end if not (fromtube and fromtube.input_inventory) then return end
local slotseq_mode local slotseq_mode
local exact_match local exmatch_mode
local filters = {} local filters = {}
if data.digiline then if data.digiline then
@ -251,10 +141,10 @@ local function punch_filter(data, filtpos, filtnode, msg)
local exmatch = msg.exmatch local exmatch = msg.exmatch
local t_exmatch = type(exmatch) local t_exmatch = type(exmatch)
if t_exmatch == "number" and exmatch >= 0 and exmatch <= 1 then if t_exmatch == "number" and (exmatch == 0 or exmatch == 1) then
exact_match = exmatch exmatch_mode = exmatch
elseif t_exmatch == "boolean" then elseif t_exmatch == "boolean" then
exact_match = exmatch and 1 or 0 exmatch_mode = exmatch and 1 or 0
end end
local slotseq_index = msg.slotseq_index local slotseq_index = msg.slotseq_index
@ -271,11 +161,11 @@ local function punch_filter(data, filtpos, filtnode, msg)
filtmeta:set_int("slotseq_mode", slotseq_mode) filtmeta:set_int("slotseq_mode", slotseq_mode)
end end
if exact_match ~= nil then if exmatch_mode ~= nil then
filtmeta:set_int("exmatch_mode", exact_match) filtmeta:set_int("exmatch_mode", exmatch_mode)
end end
if slotseq_mode ~= nil or exact_match ~= nil then if slotseq_mode ~= nil or exmatch_mode ~= nil then
set_filter_formspec(data, filtmeta) set_filter_formspec(data, filtmeta)
end end
@ -313,8 +203,8 @@ local function punch_filter(data, filtpos, filtnode, msg)
slotseq_mode = filtmeta:get_int("slotseq_mode") slotseq_mode = filtmeta:get_int("slotseq_mode")
end end
if exact_match == nil then if exmatch_mode == nil then
exact_match = filtmeta:get_int("exmatch_mode") exmatch_mode = filtmeta:get_int("exmatch_mode")
end end
local frominv local frominv
@ -328,10 +218,120 @@ local function punch_filter(data, filtpos, filtnode, msg)
frominv = frommeta:get_inventory() frominv = frommeta:get_inventory()
end end
if fromtube.before_filter then fromtube.before_filter(frompos) end if fromtube.before_filter then fromtube.before_filter(frompos) end
local function grabAndFire(frominvname, filterfor)
local sposes = {}
if not frominvname or not frominv:get_list(frominvname) then return end
for spos,stack in ipairs(frominv:get_list(frominvname)) do
local matches
if filterfor == "" then
matches = stack:get_name() ~= ""
else
local fname = filterfor.name
local fgroup = filterfor.group
local fwear = filterfor.wear
local fmetadata = filterfor.metadata
matches = (not fname -- If there's a name filter,
or stack:get_name() == fname) -- it must match.
and (not fgroup -- If there's a group filter,
or (type(fgroup) == "string" -- it must be a string
and minetest.get_item_group( -- and it must match.
stack:get_name(), fgroup) ~= 0))
and (not fwear -- If there's a wear filter:
or (type(fwear) == "number" -- If it's a number,
and stack:get_wear() == fwear) -- it must match.
or (type(fwear) == "table" -- If it's a table:
and (not fwear[1] -- If there's a lower bound,
or (type(fwear[1]) == "number" -- it must be a number
and fwear[1] <= stack:get_wear())) -- and it must be <= the actual wear.
and (not fwear[2] -- If there's an upper bound
or (type(fwear[2]) == "number" -- it must be a number
and stack:get_wear() < fwear[2])))) -- and it must be > the actual wear.
-- If the wear filter is of any other type, fail.
and (not fmetadata -- If there's a metadata filter,
or (type(fmetadata) == "string" -- it must be a string
and stack:get_metadata() == fmetadata)) -- and it must match.
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()
if fromtube.can_remove then
doRemove = fromtube.can_remove(frompos, fromnode, stack, dir, frominvname, spos)
elseif fromdef.allow_metadata_inventory_take then
doRemove = fromdef.allow_metadata_inventory_take(frompos, frominvname,spos, stack, fakePlayer)
end
-- stupid lack of continue statements grumble
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 count
if data.stackwise then
count = math.min(stack:get_count(), doRemove)
if filterfor.count and (filterfor.count > 1 or data.digiline) then
if exmatch_mode ~= 0 and filterfor.count > count then
return false -- not enough, fail
else
-- limit quantity to filter amount
count = math.min(filterfor.count, count)
end
end
else
count = 1
end
if fromtube.remove_items then
-- it could be the entire stack...
item = fromtube.remove_items(frompos, fromnode, stack, dir, count, frominvname, spos)
else
item = stack:take_item(count)
frominv:set_stack(frominvname, spos, stack)
if fromdef.on_metadata_inventory_take then
fromdef.on_metadata_inventory_take(frompos, frominvname, spos, item, fakePlayer)
end
end
local pos = vector.add(frompos, vector.multiply(dir, 1.4))
local start_pos = vector.add(frompos, dir)
local item1 = pipeworks.tube_inject_item(pos, start_pos, dir, item, fakePlayer:get_player_name())
return true -- only fire one item, please
end
end
return false
end
for _, frominvname in ipairs(type(fromtube.input_inventory) == "table" and fromtube.input_inventory or {fromtube.input_inventory}) do for _, frominvname in ipairs(type(fromtube.input_inventory) == "table" and fromtube.input_inventory or {fromtube.input_inventory}) do
local done = false local done = false
for _, filterfor in ipairs(filters) do for _, filterfor in ipairs(filters) do
if grabAndFire(data, slotseq_mode, exact_match, filtmeta, frominv, frominvname, frompos, fromnode, filterfor, fromtube, fromdef, dir, fakePlayer, data.stackwise, data.digiline) then if grabAndFire(frominvname, filterfor) then
done = true done = true
break break
end end