Minecraft Wiki
편집 요약 없음
잔글편집 요약 없음
 
(사용자 4명의 중간 판 26개는 보이지 않습니다)
6번째 줄: 6번째 줄:
 
moduleSlot = [[모듈:Inventory slot]],
 
moduleSlot = [[모듈:Inventory slot]],
 
moduleText = [[모듈:Text]],
 
moduleText = [[모듈:Text]],
  +
queryCategory = '$1 요구 제작법', -- 모듈:Crafting과 일치시킬 것 (구문을 Recipe using $1 에서 변경 시 Crafting usage가 작동하지 않음.)
queryCategory = '$1을(를) 사용하는 제작법',
 
templateCrafting = 'Crafting',
+
templateCrafting = 'Crafting', -- 한글로 바꾸지 말 것(틀 이름)
 
}
 
}
 
p.i18n = i18n
 
p.i18n = i18n
30번째 줄: 30번째 줄:
 
local prefixes = slot.i18n.prefixes
 
local prefixes = slot.i18n.prefixes
   
local function map(tbl, func)
+
local function map(tbl, func)
local newtbl = {}
+
local newtbl = {}
for i,v in pairs(tbl) do
+
for i,v in pairs(tbl) do
newtbl[i] = func(v)
+
newtbl[i] = func(v)
end
+
end
return newtbl
+
return newtbl
end
+
end
  +
 
-- Flatten a nested array, only doing the numerically-indexed parts.
+
-- Flatten a nested array, only doing the numerically-indexed parts.
local function flatten(tbl)
+
local function flatten(tbl)
local newtbl = {}
+
local newtbl = {}
local function _flat(arr)
+
local function _flat(arr)
for _, v in ipairs(arr) do
+
for _, v in ipairs(arr) do
if type(v) == "table" and v[1] then
+
if type(v) == "table" and v[1] then
_flat(v)
+
_flat(v)
else
+
else
table.insert(newtbl, v)
+
table.insert(newtbl, v)
end
+
end
end
+
end
end
+
end
_flat(tbl)
+
_flat(tbl)
return newtbl
+
return newtbl
end
+
end
  +
 
--[[Escapes special characters in ingredient names, and returns the correct
 
--[[Escapes special characters in ingredient names, and returns the correct
 
pattern depending on the match type
 
pattern depending on the match type
146번째 줄: 147번째 줄:
   
 
-- Loops through the wanted ingredients, and checks if the name contains it
 
-- Loops through the wanted ingredients, and checks if the name contains it
-- REQUIREMENT: name starts and ends with the NUL (\0) character. Simplifies operation
+
-- REQUIREMENT: name starts and ends with the NUL (\0) character. Simplifies operation
 
-- for multiple names in the variable.
 
-- for multiple names in the variable.
 
local function containsIngredients( name, ingredientPatterns )
 
local function containsIngredients( name, ingredientPatterns )
164번째 줄: 165번째 줄:
 
required frame numbers, or true if all of them are required
 
required frame numbers, or true if all of them are required
 
--]]
 
--]]
local function findRequiredFrameNums( parsedCArgs, ingredientPatterns )
+
local function findRequiredFrameNums( parsedCArgs, ingredientPatterns )
local requiredFrameNums = {}
+
local requiredFrameNums = {}
local hasRequiredFrames
+
local hasRequiredFrames
for arg, frames in pairs( parsedCArgs ) do
+
for arg, frames in pairs( parsedCArgs ) do
if arg ~= 'Output' then
+
if arg ~= 'Output' then
local requiredFrames = {}
+
local requiredFrames = {}
local count = 0
+
local count = 0
for i, frame in ipairs( frames ) do
+
for i, frame in ipairs( frames ) do
-- Guess what? If we only take the first we lose the subframes.
+
-- Guess what? If we only take the first we lose the subframes.
-- And then 'Cobblestone or Blackstone' starts breaking.
+
-- And then 'Cobblestone or Blackstone' starts breaking.
local tframe = frame[1] and flatten(frame) or { frame }
+
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'
+
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
+
if containsIngredients( names, ingredientPatterns ) then
requiredFrames[i] = true
+
requiredFrames[i] = true
count = count + 1
+
count = count + 1
end
+
end
end
+
end
if count > 0 then
+
if count > 0 then
if count == #frames then
+
if count == #frames then
return true
+
return true
end
+
end
+
hasRequiredFrames = true
+
hasRequiredFrames = true
requiredFrames.count = count
+
requiredFrames.count = count
requiredFrameNums[arg] = requiredFrames
+
requiredFrameNums[arg] = requiredFrames
end
+
end
end
+
end
end
+
end
 
 
return hasRequiredFrames and requiredFrameNums
+
return hasRequiredFrames and requiredFrameNums
 
end
 
end
   
399번째 줄: 400번째 줄:
 
end
 
end
   
--[[Performs the DPL query which retrieves the arguments from the crafting
+
--[[Performs the DPL query which retrieves the arguments from the crafting
templates on the pages within the requested categories.
+
templates on the pages within the requested categories.
If more than four categories are given, break them down into batches of four.
+
If more than four categories are given, break them down into batches of four.
--]]
+
--]]
local function dplQueryWrapper( category, ignore )
+
local function dplQueryWrapper( category, ignore )
local data = {}
+
local data = {}
if type(category) == 'string' then
+
if type(category) == 'string' then
category = mw.text.split(category, '|')
+
category = mw.text.split(category, '|')
else
+
else
assert(type(category) == 'table')
+
assert(type(category) == 'table')
end
+
end
  +
 
local includeStr = '{' .. i18n.templateCrafting .. '}:' .. table.concat( argList, ':' )
+
local includeStr = '{' .. i18n.templateCrafting .. '}:' .. table.concat( argList, ':' )
local j = 1
+
local j = 1
local count = #category
+
local count = #category
local catParts = text.split( i18n.queryCategory, '%$1' )
+
local catParts = text.split( i18n.queryCategory, '%$1' )
for i = 1, count, 4 do
+
for i = 1, count, 4 do
local catSlice = table.concat(category, '|', i, math.min(i + 3, count))
+
local catSlice = table.concat(category, '|', i, math.min(i + 3, count))
data[j] = mw.getCurrentFrame():callParserFunction( '#dpl:', {
+
data[j] = mw.getCurrentFrame():callParserFunction( '#dpl:', {
category = catSlice,
+
category = catSlice,
nottitleregexp = ignore,
+
nottitleregexp = ignore,
include = includeStr,
+
include = includeStr,
mode = 'userformat',
+
mode = 'userformat',
secseparators = '====',
+
secseparators = '====',
multisecseparators = '===='
+
multisecseparators = '===='
})
+
})
j = j + 1
+
j = j + 1
end
+
end
  +
 
return table.concat(data)
+
return table.concat(data)
 
end
 
end
  +
   
 
--[[The main body, which retrieves the data, and returns the relevant
 
--[[The main body, which retrieves the data, and returns the relevant
446번째 줄: 448번째 줄:
 
 
 
local data
 
local data
if args.category then
+
if args.category then
data = dplQueryWrapper( args.category, args.ignore )
+
data = dplQueryWrapper( args.category, args.ignore )
else
+
else
  +
-- 영어 이름을 지정해도 제작이름을 한국어화
-- Need to format the catparts
 
  +
local ingredients_ko = {}
local catParts = text.split( i18n.queryCategory, '%$1' )
 
local cats = map(ingredients, function (s)
+
for i, ingredient in ipairs( ingredients ) do
  +
ingredients_ko[i] = Autolink.invlink( ingredient, 'nolink' )
return catParts[1] .. s .. catParts[2]
 
end)
+
end
  +
ingredients = ingredients_ko
data = dplQueryWrapper( cats, args.ignore )
 
 
 
-- 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
 
end
 
 

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