|
|
| Line 1: |
Line 1: |
| --[[
| |
| Test page listing important test cases, used to preview before saving any changes.
| |
| Template:Crafting_usage/test_page
| |
| --]]
| |
|
| |
|
| 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
| |