Minejango2 (토론 | 기여) 잔글편집 요약 없음 |
Minejango2 (토론 | 기여) 잔글편집 요약 없음 |
||
(같은 사용자의 중간 판 4개는 보이지 않습니다) | |||
1번째 줄: | 1번째 줄: | ||
local p = {} |
local p = {} |
||
+ | |||
− | -- 탐색하는 아이템명 한글화 하지 말 것: 원래 외부로 노출되지 않는 분류고, Autolink를 사용하면 Autolink가 고장났을 때 이 모듈도 같이 고장남 |
||
local i18n = { |
local i18n = { |
||
emptyCategory = '빈 제작법 사용', |
emptyCategory = '빈 제작법 사용', |
||
6번째 줄: | 6번째 줄: | ||
moduleSlot = [[모듈:Inventory slot]], |
moduleSlot = [[모듈:Inventory slot]], |
||
moduleText = [[모듈:Text]], |
moduleText = [[모듈:Text]], |
||
− | queryCategory = ' |
+ | queryCategory = '$1 요구 제작법', -- 모듈:Crafting과 일치시킬 것 (구문을 Recipe using $1 에서 변경 시 Crafting usage가 작동하지 않음.) |
templateCrafting = 'Crafting', -- 한글로 바꾸지 말 것(틀 이름) |
templateCrafting = 'Crafting', -- 한글로 바꾸지 말 것(틀 이름) |
||
} |
} |
||
p.i18n = i18n |
p.i18n = i18n |
||
+ | |||
+ | local Autolink = require( [[모듈:Autolink]] ) |
||
+ | local Reverselink = require( [[모듈:Reverselink]] ) |
||
local text = require( i18n.moduleText ) |
local text = require( i18n.moduleText ) |
||
72번째 줄: | 75번째 줄: | ||
local patterns = {} |
local patterns = {} |
||
for i, ingredient in ipairs( ingredients ) do |
for i, ingredient in ipairs( ingredients ) do |
||
− | local escaped = ingredient:gsub( '([^%w])', patternChars ) |
+ | local escaped = Reverselink.xlink( ingredient:gsub( '([^%w])', patternChars ) ) |
if not matchTypes then |
if not matchTypes then |
||
patterns[i] = '%z' .. escaped .. '%z' |
patterns[i] = '%z' .. escaped .. '%z' |
||
448번째 줄: | 451번째 줄: | ||
data = dplQueryWrapper( args.category, args.ignore ) |
data = dplQueryWrapper( args.category, args.ignore ) |
||
else |
else |
||
+ | -- 영어 이름을 지정해도 제작이름을 한국어화 |
||
+ | local ingredients_ko = {} |
||
+ | for i, ingredient in ipairs( ingredients ) do |
||
+ | ingredients_ko[i] = Autolink.invlink( ingredient, 'nolink' ) |
||
+ | end |
||
+ | ingredients = ingredients_ko |
||
+ | |||
-- Need to format the catparts |
-- Need to format the catparts |
||
local catParts = text.split( i18n.queryCategory, '%$1' ) |
local catParts = text.split( i18n.queryCategory, '%$1' ) |
2021년 9월 13일 (월) 00:49 기준 최신판
이 모듈은 {{crafting usage}}
를 구현한다.
제작법의 탐색은 "~을(를) 사용하는 제작법"에 분류되는 아이템들을 탐색하여 제작법을 가져온다. (흑마법!)
의존 관계
위 모듈에 의해 정의되는 "Recipe using ~" 분류를 탐색하여 어떤 아이템이 해당 재료를 제작재료로 사용하는지를 알아낸다. 따라서 위 모듈이 생성하는 분류 이름에 모듈:Crafting usage의 코드를 맞추어야 한다.
[보기 | 편집 | 역사 | 캐시 제거]위 설명문서는 모듈:Crafting usage/doc에서 왔습니다.
local p = {}
local i18n = {
emptyCategory = '빈 제작법 사용',
moduleCrafting = [[모듈:Crafting]],
moduleSlot = [[모듈:Inventory slot]],
moduleText = [[모듈:Text]],
queryCategory = '$1 요구 제작법', -- 모듈:Crafting과 일치시킬 것 (구문을 Recipe using $1 에서 변경 시 Crafting usage가 작동하지 않음.)
templateCrafting = 'Crafting', -- 한글로 바꾸지 말 것(틀 이름)
}
p.i18n = i18n
local Autolink = require( [[모듈:Autolink]] )
local Reverselink = require( [[모듈:Reverselink]] )
local text = require( i18n.moduleText )
local slot = require( i18n.moduleSlot )
local crafting = require( i18n.moduleCrafting )
local argList = {
'ignoreusage', 'upcoming', 'name', 'ingredients', 'arggroups',
1, 2, 3, 4, 5, 6, 7, 8, 9,
'A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3',
'Output', 'description', 'fixed', 'notfixed',
'A1title', 'A1link', 'B1title', 'B1link', 'C1title', 'C1link',
'A2title', 'A2link', 'B2title', 'B2link', 'C2title', 'C2link',
'A3title', 'A3link', 'B3title', 'B3link', 'C3title', 'C3link',
'Otitle', 'Olink',
'%PAGE%',
}
local prefixes = slot.i18n.prefixes
local function map(tbl, func)
local newtbl = {}
for i,v in pairs(tbl) do
newtbl[i] = func(v)
end
return newtbl
end
-- Flatten a nested array, only doing the numerically-indexed parts.
local function flatten(tbl)
local newtbl = {}
local function _flat(arr)
for _, v in ipairs(arr) do
if type(v) == "table" and v[1] then
_flat(v)
else
table.insert(newtbl, v)
end
end
end
_flat(tbl)
return newtbl
end
--[[Escapes special characters in ingredient names, and returns the correct
pattern depending on the match type
--]]
local function createIngredientPatterns( ingredients, matchTypes )
local patternChars = {
['^'] = '%^';
['$'] = '%$';
['('] = '%(';
[')'] = '%)';
['%'] = '%%';
['.'] = '%.';
['['] = '%[';
[']'] = '%]';
['*'] = '%*';
['+'] = '%+';
['-'] = '%-';
['?'] = '%?';
['\0'] = '%z';
}
local patterns = {}
for i, ingredient in ipairs( ingredients ) do
local escaped = Reverselink.xlink( ingredient:gsub( '([^%w])', patternChars ) )
if not matchTypes then
patterns[i] = '%z' .. escaped .. '%z'
else
local matchType = matchTypes[i] or matchTypes
if matchType == 'start' then
patterns[i] = '%z' .. escaped
elseif matchType == 'end' then
patterns[i] = escaped .. '%z'
else
patterns[i] = escaped
end
end
end
return patterns
end
--[[Extracts the anonymous pipe-delimited arguments from the
DPL query into a table with the corresponding keys, skipping
templates with `ignoreusage` set, and skipping duplicate templates
--]]
local extractArgs
do
local seen = {}
extractArgs = function( template )
-- Check for duplicate template or `ignoreusage` arg
if seen[template] or not template:find( '^\n|' ) then
return
end
seen[template] = true
local tArgs = {}
local i = 1
for arg in text.gsplit( template, '\n|' ) do
if arg ~= '' then
tArgs[argList[i]] = arg
end
i = i + 1
end
tArgs.nocat = '1'
return tArgs
end
end
--[[Loops through the crafting args and parses them, with alias reference data
Identical slots reuse the same table, to allow them to be compared like strings
--]]
local function parseCraftingArgs( cArgs )
local parsedFrameText = {}
local parsedCArgs = {}
for arg, frameText in pairs( cArgs ) do
if frameText then
local randomise = arg == 'Output' and 'never' or nil
local frames = not randomise and parsedFrameText[frameText]
if not frames then
frames = slot.parseFrameText( frameText, randomise, true )
parsedFrameText[frameText] = frames
end
parsedCArgs[arg] = frames
end
end
return parsedCArgs
end
-- Loops through the wanted ingredients, and checks if the name contains it
-- REQUIREMENT: name starts and ends with the NUL (\0) character. Simplifies operation
-- for multiple names in the variable.
local function containsIngredients( name, ingredientPatterns )
for _, ingredient in pairs( ingredientPatterns ) do
if name:find( ingredient ) then
return true
end
end
return false
end
--[[Loops through the crafting ingredients and find which parameters and
frames contain the wanted ingredients
Returns a table if any matches are found, the table contains tables of
required frame numbers, or true if all of them are required
--]]
local function findRequiredFrameNums( parsedCArgs, ingredientPatterns )
local requiredFrameNums = {}
local hasRequiredFrames
for arg, frames in pairs( parsedCArgs ) do
if arg ~= 'Output' then
local requiredFrames = {}
local count = 0
for i, frame in ipairs( frames ) do
-- Guess what? If we only take the first we lose the subframes.
-- And then 'Cobblestone or Blackstone' starts breaking.
local tframe = frame[1] and flatten(frame) or { frame }
local names = '\0' .. table.concat(map(tframe, function (fr) return type(fr) == 'table' and fr.name or '' end), '\0') .. '\0'
if containsIngredients( names, ingredientPatterns ) then
requiredFrames[i] = true
count = count + 1
end
end
if count > 0 then
if count == #frames then
return true
end
hasRequiredFrames = true
requiredFrames.count = count
requiredFrameNums[arg] = requiredFrames
end
end
end
return hasRequiredFrames and requiredFrameNums
end
--[[Generates the argument groups, either using the template's specified
groups, or automatically based on the number of frames in each slot
--]]
local function generateArgGroups( predefinedArgGroups, parsedCArgs )
local argGroups = {}
if predefinedArgGroups or '' ~= '' then
local i = 1
for argGroup in text.gsplit( predefinedArgGroups, '%s*;%s*' ) do
local groupData = { args = {} }
for arg in text.gsplit( argGroup, '%s*,%s*' ) do
arg = tonumber( arg ) or arg
if not groupData.count then
groupData.count = #parsedCArgs[arg]
end
groupData.args[arg] = true
end
argGroups[i] = groupData
i = i + 1
end
else
for arg, frames in pairs( parsedCArgs ) do
local framesLen = #frames
if framesLen > 0 then
local groupName = framesLen
local alias = frames.aliasReference and frames.aliasReference[1]
if alias and alias.length == framesLen and
alias.frame.name:find( '^' .. prefixes.any .. ' ' )
then
groupName = alias.frame.name
end
local groupData = argGroups[groupName]
if not groupData then
groupData = {
args = {},
count = framesLen
}
argGroups[groupName] = groupData
end
groupData.args[arg] = true
end
end
end
return argGroups
end
--[[Adds together the required frames from each slot in this group
to get the total amount of frames which are relevant
Returns a table with the relevant frame numbers, if any are relevant
--]]
local function findRelevantFrameNums( requiredFrameNumData, group )
local relevantFrameNums = {}
local hasRelevantFrames
for arg in pairs( group ) do
local requiredFrameNums = requiredFrameNumData[arg]
if requiredFrameNums and arg ~= 'Output' then
for frameNum in pairs( requiredFrameNums ) do
-- Have to use pairs as it contains a non-sequential set of numbers
-- so we have to skip over the extra data in the table
if frameNum ~= 'count' then
hasRelevantFrames = true
relevantFrameNums[frameNum] = true
end
end
relevantFrameNums.count = math.max(
requiredFrameNums.count or 0,
relevantFrameNums.count or 0
)
end
end
return hasRelevantFrames and relevantFrameNums
end
--[[Loops through the relevant frame numbers and extracts them
into a new table, taking care of moving any alias references
and cleaning up any unnecessary subframes
--]]
function p.extractRelevantFrames( relevantFrameNums, frames )
local relevantFrames = { randomise = frames.randomise }
local newFrameNum = 1
for frameNum, frame in ipairs( frames ) do
local relevantFrame = relevantFrameNums == true or relevantFrameNums[frameNum]
if relevantFrame then
if not frame[1] then
local alias = frames.aliasReference and frames.aliasReference[frameNum]
local moveAlias = true
if alias and relevantFrameNums ~= true then
for i = frameNum, alias.length do
if not relevantFrameNums[i] then
moveAlias = false
break
end
end
end
if alias and moveAlias then
if not relevantFrames.aliasReference then
relevantFrames.aliasReference = {}
end
relevantFrames.aliasReference[newFrameNum] = alias
end
end
relevantFrames[newFrameNum] = frame
newFrameNum = newFrameNum + 1
end
end
-- Move frames in subframe to main frames, if the subframe
-- is the only frame
if not relevantFrames[2] and relevantFrames[1][1] then
relevantFrames = relevantFrames[1]
end
return relevantFrames
end
--[[Works out what data is relevant to the requested ingredients
If the template contains any of the ingredients, returns it with any
necessary modifications, and with the crafting arguments parsed
--]]
function p.processTemplate( tArgs, ingredientPatterns )
local cArgs = {}
for i, v in pairs( crafting.cArgVals ) do
cArgs[i] = tArgs[i] or tArgs[v]
end
cArgs.Output = tArgs.Output
local parsedCArgs = parseCraftingArgs( cArgs )
local requiredFrameNumData = findRequiredFrameNums( parsedCArgs, ingredientPatterns )
if not requiredFrameNumData then
return
end
local newCArgs
local modified
if requiredFrameNumData == true then
newCArgs = parsedCArgs
else
local argGroups = generateArgGroups( tArgs.arggroups, parsedCArgs )
newCArgs = {}
for _, groupData in pairs( argGroups ) do
local group = groupData.args
local relevantFrameNums = findRelevantFrameNums( requiredFrameNumData, group )
if not relevantFrameNums then
for arg in pairs( group ) do
newCArgs[arg] = parsedCArgs[arg]
end
else
modified = true
for arg in pairs( group ) do
newCArgs[arg] = p.extractRelevantFrames( relevantFrameNums, parsedCArgs[arg] )
end
end
end
end
-- Convert arguments back to shapeless format if they were originally
if tArgs[1] then
local i = 1
for argNum = 1, 9 do
tArgs[argNum] = nil
local cArg = newCArgs[argNum]
if cArg then
tArgs[i] = cArg
i = i + 1
end
end
else
for i, arg in pairs( crafting.cArgVals ) do
tArgs[arg] = newCArgs[i]
end
end
tArgs.Output = newCArgs.Output
tArgs.parsed = true
-- Let Module:Recipe table generate these
-- with the modified crafting args
if modified then
tArgs.name = nil
tArgs.ingredients = nil
end
return tArgs
end
--[[Works out which frame is the first frame, and returns its
name, or alias name, for sorting purposes
--]]
function p.getFirstFrameName( frames, subframe )
local frame = frames[1]
if not subframe and frame[1] then
return p.getFirstFrameName( frame[1], true )
end
local alias = frames.aliasReference and frames.aliasReference[1]
return alias and alias.frame.name or frame.name
end
--[[Performs the DPL query which retrieves the arguments from the crafting
templates on the pages within the requested categories.
If more than four categories are given, break them down into batches of four.
--]]
local function dplQueryWrapper( category, ignore )
local data = {}
if type(category) == 'string' then
category = mw.text.split(category, '|')
else
assert(type(category) == 'table')
end
local includeStr = '{' .. i18n.templateCrafting .. '}:' .. table.concat( argList, ':' )
local j = 1
local count = #category
local catParts = text.split( i18n.queryCategory, '%$1' )
for i = 1, count, 4 do
local catSlice = table.concat(category, '|', i, math.min(i + 3, count))
data[j] = mw.getCurrentFrame():callParserFunction( '#dpl:', {
category = catSlice,
nottitleregexp = ignore,
include = includeStr,
mode = 'userformat',
secseparators = '====',
multisecseparators = '===='
})
j = j + 1
end
return table.concat(data)
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 ingredients = args[1] and text.split( args[1], '%s*,%s*' ) or { mw.title.getCurrentTitle().text }
local matchTypes = args.match and args.match:find( ',' ) and text.split( args.match, '%s*,%s*' ) or args.match
local ingredientPatterns = createIngredientPatterns( ingredients, matchTypes )
local data
if args.category then
data = dplQueryWrapper( args.category, args.ignore )
else
-- 영어 이름을 지정해도 제작이름을 한국어화
local ingredients_ko = {}
for i, ingredient in ipairs( ingredients ) do
ingredients_ko[i] = Autolink.invlink( ingredient, 'nolink' )
end
ingredients = ingredients_ko
-- Need to format the catparts
local catParts = text.split( i18n.queryCategory, '%$1' )
local cats = map(ingredients, function (s)
return catParts[1] .. s .. catParts[2]
end)
data = dplQueryWrapper( cats, args.ignore )
end
local showDescription
local templates = {}
local i = 1
for templateArgs in text.gsplit( data:sub( 5 ), '====' ) do
local tArgs = extractArgs( templateArgs )
local newArgs = tArgs and p.processTemplate( tArgs, ingredientPatterns )
if newArgs then
if tArgs.description then
showDescription = '1'
end
templates[i] = {
args = newArgs,
sortKey = mw.ustring.lower(
( newArgs.name or p.getFirstFrameName( newArgs.Output ) )
:gsub( '^' .. prefixes.any .. ' ', '' )
:gsub( '^' .. prefixes.matching .. ' ', '' )
:gsub( '^%[%[', '' )
:gsub( '^[^|]+|', '' )
),
}
i = i + 1
end
end
local templateCount = #templates
if templateCount == 0 then
return f:expandTemplate{ title = 'Translation category', args = { i18n.emptyCategory, project = '0' } }
end
table.sort( templates, function( a, b )
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] = ( '<!-- [[' .. template.args['%PAGE%'] .. ']] -->\n'
.. crafting.table( template.args ) )
end
return table.concat( out, '\n' )
end
return p