Diferencia entre revisiones de «Módulo:Crafting usage»
| (No se muestran 9 ediciones intermedias de 4 usuarios) | |||
| 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 | ||
| − | + | -- 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 actual del 16: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