Diferencia entre revisiones de «Módulo:Crafting usage»
Línea 1: | Línea 1: | ||
+ | |||
local p = {} | 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 | |
− | local | ||
− | |||
− | + | -- 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 | + | local allFrames = {} -- All input frames |
− | + | local allFrameCount = 0 | |
− | function | + | local frameCount = 0 -- Count how many expanded frames we are in our iteration. Used for restricting output frames to match input frames |
− | |||
− | if | ||
− | |||
− | |||
− | local | ||
− | local | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | 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 | + | local isAlias = (expandedAlias and expandedAlias[1]) ~= nil |
− | local | + | 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 | end | ||
+ | elseif isAlias == false or subframe == true then | ||
+ | frameCount = frameCount + 1 | ||
end | 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 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | if | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | 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 | 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 #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 | + | local j = math.random(i) |
− | + | replacementString[i], replacementString[j] = replacementString[j], replacementString[i] | |
− | |||
− | |||
− | |||
− | |||
− | |||
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 | + | -- Save modified frame to proper location |
− | if | + | 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 | ||
− | + | 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 | |
− | + | 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 | end | ||
− | + | local newArgs = filterFrames(tArgs, startingIngredients) | |
− | + | table.insert(templates,{args = newArgs, sortKey = newArgs.Output or newArgs.name}) | |
− | |||
− | |||
end | end | ||
+ | else | ||
+ | mw.log("ERROR: query returned table.") | ||
+ | mw.logObject(v['Crafting JSON']) | ||
end | end | ||
end | end | ||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
end | end | ||
− | + | ||
− | return ' | + | 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 | + | table.sort(templates, function(a, b) |
− | templates[1]. | + | 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 | if not args.continue then | ||
− | templates[ | + | templates[templateCount].args.foot = '1' |
end | end | ||
− | |||
local out = {} | local out = {} | ||
− | for i, | + | for i, template in ipairs(templates) do |
− | + | 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 del 15:47 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