Diferencia entre revisiones de «Módulo:Crafting usage»

De WikiMinecraft
Saltar a: navegación, buscar
 
(No se muestra una edición intermedia del mismo usuario)
Línea 1: Línea 1:
 
local p = {}
 
local p = {}
function p.dpl( f )
+
local titleText = mw.title.getCurrentTitle().text
local args = f:getParent().args
 
local grid = require( 'Module:Grid' )
 
local ingredients = args[1] and mw.text.split( args[1], ',' ) or { mw.title.getCurrentTitle().text }
 
local matchTypes = args.match and args.match:find( ',' ) and mw.text.split( args.match, ',' ) or args.match
 
  
local argList = {
+
local i18n = {
'ignoreusage', 'upcoming', 'name', 'ingredients', 'arggroups',
+
emptyCategory = 'Empty crafting usage',
1, 2, 3, 4, 5, 6, 7, 8, 9,
+
moduleCrafting = [[Module:Crafting]],
'A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3',
+
moduleSlot = [[Module:Inventory slot]],
'Output', 'description'
+
moduleAliases = [[Module:Inventory slot/Aliases]],
}
+
moduleText = [[Module:Text]],
local anonToShaped = { 'A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3' }
+
templateCrafting = 'Crafting',
local shapedToAnon = { A1 = 1, B1 = 2, C1 = 3, A2 = 4, B2 = 5, C2 = 5, A3 = 6, B3 = 7, C3 = 8 }
+
}
 +
p.i18n = i18n
 +
 
 +
local text = require(i18n.moduleText)
 +
local slot = require(i18n.moduleSlot)
 +
local aliases = require(i18n.moduleAliases)
 +
local crafting = require(i18n.moduleCrafting)
 +
 
 +
local function filterFrames(craftingArgs, pinnedItems)
 +
-- Create a lookup table to easily determine if a string is a member of pinned items.
 +
local pinnedItemsLookup = {}
 +
for _, entry in pairs(pinnedItems) do
 +
pinnedItemsLookup[entry] = true
 +
end
 
 
local data = f:callParserFunction( '#dpl', {
+
-- Find any frames in this string that need to be pinned and pin them.
category = args.category or 'Recetas que usan ' .. table.concat( ingredients, '|Recetas que usan ' ),
+
local outputPinnedFrameCount = {}
nottitleregexp = args.ignore,
+
local shouldPinOutput = false
include = '{Crafting}:' .. table.concat( argList, ':' ),
+
local function pinFrames(inputString, restrictToMatching, isOutput)
mode = 'userformat',
+
if not inputString then return nil end
secseparators = '====',
+
isOutput = isOutput or false
multisecseparators = '===='
+
} )
+
local pinFrames = {} -- Only frames that are pinned
+
local pinnedFrameCount = 0
-- Comment this next line out if you're not using aliases
+
local pinnedFramesIndex = {}
local aliases = mw.loadData( 'Module:Grid/Aliases' )
+
local allFrames = {} -- All input frames
+
local allFrameCount = 0
function matchPattern( ingredient, ingredientNum )
+
local frameCount = 0 -- Count how many expanded frames we are in our iteration. Used for restricting output frames to match input frames
local matchType = matchTypes
 
if type( matchType ) == 'table' then
 
matchType = matchTypes[ingredientNum]
 
end
 
local pattern
 
local escaped = ingredient:gsub( '([%(%)])', '%%%1' )
 
if matchType == 'start' then
 
pattern = '[;:%]]%s*' .. escaped
 
elseif matchType == 'end' then
 
pattern = escaped .. '%s*[,;%[]'
 
elseif matchType == 'any' then
 
pattern = escaped
 
else
 
pattern = '[;:%]]%s*' .. escaped .. '%s*[,;%[]'
 
end
 
 
 
return pattern
+
for frameStr in string.gmatch(inputString, "[^;]+") do
end
+
local shouldPin = false
local out = {}
+
frameStr = mw.text.trim(frameStr)
local showDesciption
+
local originalFrameString = frameStr
local templates = {}
+
local frameObj = slot.makeFrame(frameStr, '')
for template in mw.text.gsplit( data, '====' ) do
+
local name = (frameObj.name:gsub("[{}]", "")) -- If this name is a subframe, remove the braces for alias testing.
-- If ignoreusage is empty
+
local subframe = (frameObj.name:find("[{}]") ~= nil) -- If the current frame is a subframe, don't advance the frame count for aliases
if template:find( '^%s*|' ) then
+
local expandedAlias = aliases[name]
local tArgs = {}
+
local isAlias = (expandedAlias and expandedAlias[1]) ~= nil
local i = 0
+
local isDesiredItem = (pinnedItemsLookup[name] ~= nil) -- If this item is present in our list of items to pin
-- Extract the arguments from the DPL query
+
for tArg in mw.text.gsplit( template, '\n|' ) do
+
-- Even if we have an exactly matching alias input, we have to step through each frame to log it as pinnable.
i = i + 1
+
if isAlias == true and isDesiredItem then
if tArg ~= '' then
+
shouldPin = true -- Set this frame as adding to the list of pinned frames
local key = argList[i]
+
if not subframe then
tArgs[key] = tArg
+
for _,_ in pairs(expandedAlias) do
 +
frameCount = frameCount + 1
 +
pinnedFramesIndex[frameCount] = true
 +
end
 +
else
 +
frameCount = frameCount + 1
 +
pinnedFramesIndex[frameCount] = true
 
end
 
end
 +
elseif isAlias == false or subframe == true then
 +
frameCount = frameCount + 1
 
end
 
end
 
 
local craftingArgs = {
+
if isAlias -- If this is an alias
tArgs[1] or tArgs.A1 or '', tArgs[2] or tArgs.B1 or '', tArgs[3] or tArgs.C1 or '',
+
and not isDesiredItem -- and we don't want to keep the alias as is
tArgs[4] or tArgs.A2 or '', tArgs[5] or tArgs.B2 or '', tArgs[6] or tArgs.C2 or '',
+
and (not isOutput or (isOutput and restrictToMatching)) then -- and we are either not output, or we are output that needs to be restricted.
tArgs[7] or tArgs.A3 or '', tArgs[8] or tArgs.B3 or '', tArgs[9] or tArgs.C3 or '',
+
local replacementString = {}
Output = tArgs.Output
+
for _,candidate in pairs(expandedAlias) do -- Find all pinned items that are part of this alias
}
+
if not subframe then -- Subframes only actually count as one frame
+
frameCount = frameCount + 1
local expandedFrames = {}
 
local hasIngredient
 
local argsWithIngredient = {}
 
local argGroups = {}
 
for i, v in pairs( craftingArgs ) do
 
if v ~= '' then
 
if aliases then
 
expandedFrames[i] = {}
 
local expandedFrame = {}
 
for frame in mw.text.gsplit( v, '%s*;%s*' ) do
 
local parts = grid.getParts( frame )
 
local alias = aliases[parts.name]
 
if alias then
 
local expandedAlias = grid.expandAlias( parts, alias ):gsub( '%s*([%[%]:,;])%s*', '%1' )
 
expandedFrames[i][frame] = expandedAlias:gsub( '([%(%)])', '%%%1' )
 
table.insert( expandedFrame, expandedAlias )
 
else
 
table.insert( expandedFrame, frame )
 
end
 
end
 
 
v = table.concat( expandedFrame, ';' )
 
craftingArgs[i] = v
 
 
end
 
end
if i ~= 'Output' then
+
 
local delimitedFrames = ';' .. v .. ';'
+
candidate = candidate.name or candidate -- Sometimes alias returns a table of tables
for ingredientNum, ingredient in pairs( ingredients ) do
+
if pinnedItemsLookup[candidate] or (restrictToMatching and outputPinnedFrameCount[frameCount]) then
if delimitedFrames:find( matchPattern( ingredient, ingredientNum ) ) then
+
local candidateOut = candidate
if not v:find( ';' ) then
+
if frameObj.num then
hasIngredient = 'static'
+
candidateOut = candidateOut..','..frameObj.num
elseif not hasIngredient then
 
hasIngredient = 'animated'
 
end
 
 
argsWithIngredient[i] = true
 
end
 
 
end
 
end
 +
table.insert(replacementString, candidateOut)
 +
pinnedFramesIndex[frameCount] = true -- When we find a good entry, store the number for use by Output
 
end
 
end
+
end
if not tArgs.arggroups and hasIngredient ~= 'static' then
+
if #replacementString > 0 then -- If this alias doesn't have any pinned items in it, leave it alone.
local _, frameCount = v:gsub( ';', '' )
+
if name:find("^" .. slot.i18n.prefixes.any .. " ") then
if frameCount > 0 then
+
-- Simple in place randomization
frameCount = frameCount + 1
+
for i = #replacementString, 2, -1 do
local group = argGroups[frameCount]
+
local j = math.random(i)
if not group then
+
replacementString[i], replacementString[j] = replacementString[j], replacementString[i]
group = { args = {} }
 
argGroups[frameCount] = group
 
end
 
group.count = frameCount
 
group.args[i] = true
 
 
end
 
end
 +
else -- If we modify anything that isn't an "Any" alias, force pin the output.
 +
shouldPinOutput = true
 
end
 
end
 +
replacementString = table.concat(replacementString, ';')
 +
-- The gsub is to put a % in front of every non alphanumeric character in name, this escapes the name so special characters don't act as a pattern match.
 +
local findString = '('..name:gsub("(%W)", "%%%1")..',?%d*)' -- Also capture the quantity, since the replacement string has the quantity attached.
 +
frameStr = frameStr:gsub(findString, replacementString, 1) -- Replace the alias name in the frame with the expanded and filtered string
 +
shouldPin = true -- If we match with an expanded alias then we have to pin it
 
end
 
end
 
end
 
end
 
 
if hasIngredient then
+
-- Save modified frame to proper location
if tArgs.description then
+
if isDesiredItem or shouldPin or (restrictToMatching and outputPinnedFrameCount[frameCount]) then
showDescription = true
+
table.insert(pinFrames, frameStr)
 +
pinnedFrameCount = pinnedFrameCount + frameCount
 +
-- Don't pin an alias, unless its an exact match
 +
if not isOutput and (not isAlias and isDesiredItem) then
 +
pinnedFramesIndex[frameCount] = true -- When we find a good entry, store the number for use by Output
 
end
 
end
+
end
if hasIngredient == 'animated' then
+
table.insert(allFrames, frameStr)
if tArgs.arggroups then
+
allFrameCount = allFrameCount + frameCount
for argGroup in mw.text.gsplit( tArgs.arggroups, '%s*;%s*' ) do
+
end
local group = {}
+
if pinnedFrameCount > 0 then
local _, frameCount
+
if pinnedFrameCount < allFrameCount then
for arg in mw.text.gsplit( argGroup, '%s*,%s*' ) do
+
shouldPinOutput = true
if not tArgs[1] then
+
end
arg = shapedToAnon[arg]
+
for k,v in pairs(pinnedFramesIndex) do
end
+
outputPinnedFrameCount[k] = v
if not frameCount then
+
end
_, frameCount = craftingArgs[arg]:gsub( ';', '' )
+
return table.concat(pinFrames, ';')
end
+
else
group[arg] = true
+
return table.concat(allFrames, ';')
end
+
end
table.insert( argGroups, { count = frameCount + 1, args = group } )
+
end
end
+
 +
-- for A1, A2, A3, B1, B2, etc
 +
for _, arg in ipairs(crafting.cArgVals) do
 +
craftingArgs[arg] = pinFrames(craftingArgs[arg])
 +
end
 +
craftingArgs.Output = pinFrames(craftingArgs.Output, shouldPinOutput, true)
 +
 +
return craftingArgs
 +
end
 +
 
 +
--[[The main body, which retrieves the data, and returns the relevant
 +
crafting templates, sorted alphabetically
 +
--]]
 +
function p.dpl(f)
 +
local args = f
 +
if f == mw.getCurrentFrame() then
 +
args = f:getParent().args
 +
else
 +
f = mw.getCurrentFrame()
 +
end
 +
 +
local startingIngredients = args[1] and text.split(args[1], '%s*,%s*') or {titleText}
 +
local seen = {}
 +
local ingredients = {}
 +
 +
-- Loop through all defined ingredients, and expand any aliases.
 +
for _,entry in pairs(startingIngredients) do
 +
if not seen[entry] then
 +
table.insert(ingredients, entry)
 +
seen[entry] = true
 +
local expandedAlias = aliases[entry]
 +
 
 +
if expandedAlias then
 +
for _,a in pairs(expandedAlias) do
 +
if not seen[a] then
 +
table.insert(ingredients, (a.name or a))
 +
seen[a] = true
 
end
 
end
+
end
for _, groupData in pairs( argGroups ) do
+
end
local frameCount = groupData.count
+
end
local group = groupData.args
+
end
local requiredFrames = {}
+
local requiredFramesCount = 0
+
local showDescription
for arg in pairs( group ) do
+
local templates = {}
if argsWithIngredient[arg] then
+
local frames = craftingArgs[arg]
+
local queryStrings = {}
local frameNum = 0
+
-- SMW can query up to 15 conditions at once, so group ingredients into 15
for frame in mw.text.gsplit( frames, '%s*;%s*' ) do
+
local count = 1
frameNum = frameNum + 1
+
while count <= #ingredients do
if not requiredFrames[frameNum] then
+
queryStrings[math.floor(count/15)+1] = table.concat(ingredients, '||', count, math.min(#ingredients, count + 14))
local delimitedFrame = ';' .. frame .. ';'
+
count = count + 15
for ingredientNum, ingredient in pairs( ingredients ) do
+
end
if delimitedFrame:find( matchPattern( ingredient, ingredientNum ) ) then
+
requiredFrames[frameNum] = true
+
local seen = {}
requiredFramesCount = requiredFramesCount + 1
+
for _,str in ipairs(queryStrings) do
end
+
local query = {
end
+
'[[Crafting ingredient::'..str..']]',
end
+
'?Crafting JSON',
end
+
'?-Has subobject#-=Source page',
end
+
limit = 500
end
+
}
+
local smwdata = mw.smw.ask(query)
-- Not all frames will be used
+
if requiredFramesCount > 0 and requiredFramesCount < frameCount then
+
if smwdata then
for arg in pairs( group ) do
+
for _,v in ipairs(smwdata) do
local frames = craftingArgs[arg]
+
if v['Source page'] ~= titleText then -- Do not display results that came from the same page we are operating on, as they will be outdated.
local newFrames = {}
+
if type(v['Crafting JSON']) ~= "table" then --If a subobject name is not unique enough it will return as a table.
local frameNum = 0
+
if seen[v['Crafting JSON']] == nil then
for frame in mw.text.gsplit( frames, '%s*;%s*' ) do
+
seen[v['Crafting JSON']] = 1
frameNum = frameNum + 1
+
local tArgs = mw.text.jsonDecode(v['Crafting JSON'])
if requiredFrames[frameNum] then
+
tArgs['ignoreusage'] = '1' -- Always set ignore usage for crafting invocations that are usage.
table.insert( newFrames, frame )
+
if tArgs.description and tArgs.description ~= '' then
end
+
showDescription = '1'
end
 
newFrames = table.concat( newFrames, ';' )
 
 
-- If the whole expanded alias survived, collapse it again
 
if expandedFrames[arg] then
 
for frame, expandedAlias in pairs( expandedFrames[arg] ) do
 
--newFrames = 'blah' .. expandedAlias
 
newFrames = newFrames:gsub( expandedAlias, frame )
 
end
 
end
 
 
local tArg = arg
 
if arg ~= 'Output' and not tArgs[1] then
 
tArg = anonToShaped[arg]
 
end
 
tArgs[tArg] = newFrames
 
 
end
 
end
+
local newArgs = filterFrames(tArgs, startingIngredients)
-- Let Module:Crafting handle the name and ingredients columns
+
table.insert(templates,{args = newArgs, sortKey = newArgs.Output or newArgs.name})
tArgs.name = nil
 
tArgs.ingredients = nil
 
 
end
 
end
 +
else
 +
mw.log("ERROR: query returned table.")
 +
mw.logObject(v['Crafting JSON'])
 
end
 
end
 
end
 
end
 
tArgs.nocat = '1'
 
 
table.insert( templates, tArgs )
 
 
end
 
end
 
end
 
end
 
end
 
end
if #templates == 0 then
+
return '[[Category:Como ingrediente vacío]]'
+
local templateCount = #templates
 +
if templateCount == 0 then
 +
if mw.title.getCurrentTitle().nsText == '' then
 +
return f:expandTemplate{title='Translation category', args={i18n.emptyCategory, project='0'}}
 +
end
 +
return ''
 
end
 
end
 
 
templates[1].head = '1'
+
table.sort(templates, function(a, b)
templates[1].showname = '1'
+
if not a then return b end
if showDescription and args.showdesciption ~= '0' or args.showdesciption == '1' then
+
if not b then return a end
templates[1].showdescription = '1'
+
return a.sortKey < b.sortKey
end
+
end)
 +
 +
local initialArgs = templates[1].args
 +
initialArgs.head = '1'
 +
initialArgs.showname = '1'
 +
initialArgs.showdescription = showDescription
 
if not args.continue then
 
if not args.continue then
templates[#templates].foot = '1'
+
templates[templateCount].args.foot = '1'
 
end
 
end
 
 
local crafting = require( 'Module:Crafting' )
 
 
local out = {}
 
local out = {}
for i, v in ipairs( templates ) do
+
for i, template in ipairs(templates) do
table.insert( out, crafting.table( v ) )
+
out[i] = crafting.table(template.args)
 
end
 
end
+
return table.concat(out, '\n')
return table.concat( out, '\n' )
 
 
end
 
end
 +
 
return p
 
return p

Revisión actual del 15:49 15 dic 2024


local p = {}
local titleText = mw.title.getCurrentTitle().text

local i18n = {
	emptyCategory = 'Empty crafting usage',
	moduleCrafting = [[Module:Crafting]],
	moduleSlot = [[Module:Inventory slot]],
	moduleAliases = [[Module:Inventory slot/Aliases]],
	moduleText = [[Module:Text]],
	templateCrafting = 'Crafting',
}
p.i18n = i18n

local text = require(i18n.moduleText)
local slot = require(i18n.moduleSlot)
local aliases = require(i18n.moduleAliases)
local crafting = require(i18n.moduleCrafting)

local function filterFrames(craftingArgs, pinnedItems)
	-- Create a lookup table to easily determine if a string is a member of pinned items.
	local pinnedItemsLookup = {}
	for _, entry in pairs(pinnedItems) do
		pinnedItemsLookup[entry] = true	
	end
	
	-- Find any frames in this string that need to be pinned and pin them.
	local outputPinnedFrameCount = {}
	local shouldPinOutput = false
	local function pinFrames(inputString, restrictToMatching, isOutput)
		if not inputString then return nil end
		isOutput = isOutput or false
		
		local pinFrames = {} -- Only frames that are pinned
		local pinnedFrameCount = 0
		local pinnedFramesIndex = {}
		local allFrames = {} -- All input frames
		local allFrameCount = 0
		local frameCount = 0 -- Count how many expanded frames we are in our iteration. Used for restricting output frames to match input frames
		
		for frameStr in string.gmatch(inputString, "[^;]+") do
			local shouldPin = false
			frameStr = mw.text.trim(frameStr)
			local originalFrameString = frameStr
			local frameObj = slot.makeFrame(frameStr, '')
			local name = (frameObj.name:gsub("[{}]", "")) -- If this name is a subframe, remove the braces for alias testing.
			local subframe = (frameObj.name:find("[{}]") ~= nil) -- If the current frame is a subframe, don't advance the frame count for aliases
			local expandedAlias = aliases[name]
			local isAlias = (expandedAlias and expandedAlias[1]) ~= nil
			local isDesiredItem = (pinnedItemsLookup[name] ~= nil) -- If this item is present in our list of items to pin
			
			-- Even if we have an exactly matching alias input, we have to step through each frame to log it as pinnable.
			if isAlias == true and isDesiredItem then
				shouldPin = true -- Set this frame as adding to the list of pinned frames
				if not subframe then
					for _,_ in pairs(expandedAlias) do
						frameCount = frameCount + 1
						pinnedFramesIndex[frameCount] = true
					end
				else
					frameCount = frameCount + 1
					pinnedFramesIndex[frameCount] = true
				end
			elseif isAlias == false or subframe == true then
				frameCount = frameCount + 1
			end
			
			if isAlias -- If this is an alias
			and not isDesiredItem -- and we don't want to keep the alias as is
			and (not isOutput or (isOutput and restrictToMatching)) then -- and we are either not output, or we are output that needs to be restricted.
				local replacementString = {}
				for _,candidate in pairs(expandedAlias) do -- Find all pinned items that are part of this alias
					if not subframe then -- Subframes only actually count as one frame
						frameCount = frameCount + 1
					end

					candidate = candidate.name or candidate -- Sometimes alias returns a table of tables
					if pinnedItemsLookup[candidate] or (restrictToMatching and outputPinnedFrameCount[frameCount]) then
						local candidateOut = candidate
						if frameObj.num then
							candidateOut = candidateOut..','..frameObj.num	
						end
						table.insert(replacementString, candidateOut)
						pinnedFramesIndex[frameCount] = true -- When we find a good entry, store the number for use by Output
					end
				end
				if #replacementString > 0 then -- If this alias doesn't have any pinned items in it, leave it alone.
					if name:find("^" .. slot.i18n.prefixes.any .. " ") then
						-- Simple in place randomization
						for i = #replacementString, 2, -1 do
							local j = math.random(i)
							replacementString[i], replacementString[j] = replacementString[j], replacementString[i]
						end
					else -- If we modify anything that isn't an "Any" alias, force pin the output.
						shouldPinOutput = true
					end
					replacementString = table.concat(replacementString, ';')
					-- The gsub is to put a % in front of every non alphanumeric character in name, this escapes the name so special characters don't act as a pattern match.
					local findString = '('..name:gsub("(%W)", "%%%1")..',?%d*)' -- Also capture the quantity, since the replacement string has the quantity attached.
					frameStr = frameStr:gsub(findString, replacementString, 1) -- Replace the alias name in the frame with the expanded and filtered string
					shouldPin = true -- If we match with an expanded alias then we have to pin it
				end
			end
			
			-- Save modified frame to proper location
			if isDesiredItem or shouldPin or (restrictToMatching and outputPinnedFrameCount[frameCount]) then
				table.insert(pinFrames, frameStr)
				pinnedFrameCount = pinnedFrameCount + frameCount
				-- Don't pin an alias, unless its an exact match
				if not isOutput and (not isAlias and isDesiredItem) then
					pinnedFramesIndex[frameCount] = true -- When we find a good entry, store the number for use by Output
				end
			end
			table.insert(allFrames, frameStr)
			allFrameCount = allFrameCount + frameCount
		end
		if pinnedFrameCount > 0 then
			if pinnedFrameCount < allFrameCount then
				shouldPinOutput = true
			end
			for k,v in pairs(pinnedFramesIndex) do
				outputPinnedFrameCount[k] = v
			end
			return table.concat(pinFrames, ';')
		else
			return table.concat(allFrames, ';')
		end
	end
	
	-- for A1, A2, A3, B1, B2, etc
	for _, arg in ipairs(crafting.cArgVals) do
		craftingArgs[arg] = pinFrames(craftingArgs[arg])
	end
	craftingArgs.Output = pinFrames(craftingArgs.Output, shouldPinOutput, true)
	
	return craftingArgs
end

--[[The main body, which retrieves the data, and returns the relevant
	crafting templates, sorted alphabetically
--]]
function p.dpl(f)
	local args = f
	if f == mw.getCurrentFrame() then
		args = f:getParent().args
	else
		f = mw.getCurrentFrame()
	end
	
	local startingIngredients = args[1] and text.split(args[1], '%s*,%s*') or {titleText}
	local seen = {}
	local ingredients = {}
	
	-- Loop through all defined ingredients, and expand any aliases.
	for _,entry in pairs(startingIngredients) do
		if not seen[entry] then
			table.insert(ingredients, entry)
			seen[entry] = true
			local expandedAlias = aliases[entry]

			if expandedAlias then
				for _,a in pairs(expandedAlias) do
					if not seen[a] then
						table.insert(ingredients, (a.name or a))
						seen[a] = true	
					end
				end
			end
		end
	end
	
	local showDescription
	local templates = {}
	
	local queryStrings = {}
	-- SMW can query up to 15 conditions at once, so group ingredients into 15
	local count = 1
	while count <= #ingredients do
		queryStrings[math.floor(count/15)+1] = table.concat(ingredients, '||', count, math.min(#ingredients, count + 14))
		count = count + 15
	end
	
	local seen = {}
	for _,str in ipairs(queryStrings) do
		local query = {
			'[[Crafting ingredient::'..str..']]',
			'?Crafting JSON',
			'?-Has subobject#-=Source page',
			limit = 500
		}
		local smwdata = mw.smw.ask(query)
		
		if smwdata then
			for _,v in ipairs(smwdata) do
				if v['Source page'] ~= titleText then -- Do not display results that came from the same page we are operating on, as they will be outdated.
					if type(v['Crafting JSON']) ~= "table" then --If a subobject name is not unique enough it will return as a table.
						if seen[v['Crafting JSON']] == nil then
							seen[v['Crafting JSON']] = 1
							local tArgs = mw.text.jsonDecode(v['Crafting JSON'])
							tArgs['ignoreusage'] = '1' -- Always set ignore usage for crafting invocations that are usage.
							if tArgs.description and tArgs.description ~= '' then
								showDescription = '1'
							end
							local newArgs = filterFrames(tArgs, startingIngredients)
							table.insert(templates,{args = newArgs,	sortKey = newArgs.Output or newArgs.name})
						end
					else
						mw.log("ERROR: query returned table.")
						mw.logObject(v['Crafting JSON'])
					end
				end
			end
		end
	end
	
	local templateCount = #templates
	if templateCount == 0 then
		if mw.title.getCurrentTitle().nsText == '' then
			return f:expandTemplate{title='Translation category', args={i18n.emptyCategory, project='0'}}
		end
		return ''
	end
	
	table.sort(templates, function(a, b)
		if not a then return b end
		if not b then return a end
		return a.sortKey < b.sortKey
	end)
	
	local initialArgs = templates[1].args
	initialArgs.head = '1'
	initialArgs.showname = '1'
	initialArgs.showdescription = showDescription
	if not args.continue then
		templates[templateCount].args.foot = '1'
	end
	
	local out = {}
	for i, template in ipairs(templates) do
		out[i] = crafting.table(template.args)
	end
	return table.concat(out, '\n')
end

return p