새로 오신 분들은 길라잡이위키 규칙을 꼭 읽어주세요.

문서 작성번역을 도와주시면 이 위키에 큰 힘이 됩니다.

Fandom 측에서 진행하는 통합 커뮤니티 플랫폼으로의 이주 작업이 이루어지는 동안 일부 기능이 작동하지 않을 수 있습니다. 양해 부탁드립니다.

"모듈:Crafting usage"의 두 판 사이의 차이

Minecraft Wiki
이동: 둘러보기, 검색
잔글
30번째 줄: 30번째 줄:
 
local prefixes = slot.i18n.prefixes
 
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
 
--[[Escapes special characters in ingredient names, and returns the correct
 
pattern depending on the match type
 
pattern depending on the match type
53번째 줄: 76번째 줄:
 
local escaped = Reverselink.xlink( ingredient:gsub( '([^%w])', patternChars ) )
 
local escaped = Reverselink.xlink( ingredient:gsub( '([^%w])', patternChars ) )
 
if not matchTypes then
 
if not matchTypes then
patterns[i] = '^' .. escaped .. '$'
+
patterns[i] = '%z' .. escaped .. '%z'
 
else
 
else
 
local matchType = matchTypes[i] or matchTypes
 
local matchType = matchTypes[i] or matchTypes
 
if matchType == 'start' then
 
if matchType == 'start' then
patterns[i] = '^' .. escaped
+
patterns[i] = '%z' .. escaped
 
elseif matchType == 'end' then
 
elseif matchType == 'end' then
patterns[i] = escaped .. '$'
+
patterns[i] = escaped .. '%z'
 
else
 
else
 
patterns[i] = escaped
 
patterns[i] = escaped
123번째 줄: 146번째 줄:
   
 
-- 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
  +
-- for multiple names in the variable.
 
local function containsIngredients( name, ingredientPatterns )
 
local function containsIngredients( name, ingredientPatterns )
 
for _, ingredient in pairs( ingredientPatterns ) do
 
for _, ingredient in pairs( ingredientPatterns ) do
139번째 줄: 164번째 줄:
 
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.
if containsIngredients( frame.name or frame[1].name, ingredientPatterns ) then
 
  +
-- And then 'Cobblestone or Blackstone' starts breaking.
requiredFrames[i] = true
 
  +
local tframe = frame[1] and flatten(frame) or { frame }
count = count + 1
 
  +
local names = '\0' .. table.concat(map(tframe, function (fr) return type(fr) == 'table' and fr.name or '' end), '\0') .. '\0'
end
 
 
if containsIngredients( names, ingredientPatterns ) then
end
 
 
requiredFrames[i] = true
if count > 0 then
 
if count == #frames then
+
count = count + 1
return true
+
end
end
+
end
 
if count > 0 then
 
  +
if count == #frames then
hasRequiredFrames = true
 
  +
return true
requiredFrames.count = count
 
 
end
requiredFrameNums[arg] = requiredFrames
 
end
+
 
hasRequiredFrames = true
end
 
 
requiredFrames.count = count
end
 
 
requiredFrameNums[arg] = requiredFrames
 
 
end
return hasRequiredFrames and requiredFrameNums
 
 
end
  +
end
  +
 
return hasRequiredFrames and requiredFrameNums
 
end
 
end
   
370번째 줄: 399번째 줄:
 
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.
--]]
+
--]]
function dplQuery( category, ignore )
+
local function dplQueryWrapper( category, ignore )
return mw.getCurrentFrame():callParserFunction( '#dpl:', {
 
 
local data = {}
category = category,
 
  +
if type(category) == 'string' then
nottitleregexp = ignore,
 
  +
category = mw.text.split(category, '|')
include = '{' .. i18n.templateCrafting .. '}:' .. table.concat( argList, ':' ),
 
  +
else
mode = 'userformat',
 
  +
assert(type(category) == 'table')
secseparators = '====',
 
  +
end
multisecseparators = '===='
 
  +
} )
 
 
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
 
end
   
399번째 줄: 446번째 줄:
 
 
 
local data
 
local data
if args.category then
+
if args.category then
data = dplQuery( args.category, args.ignore )
+
data = dplQueryWrapper( args.category, args.ignore )
else
+
else
  +
-- Need to format the catparts
-- DPL has a limit of four categories, so do it in chunks
 
  +
local catParts = text.split( i18n.queryCategory, '%$1' )
data = {}
 
local dataNum = 1
+
local cats = map(ingredients, function (s)
 
return catParts[1] .. s .. catParts[2]
local ingredientCount = #ingredients
 
  +
end)
local catParts = text.split( i18n.queryCategory, '%$1' )
 
  +
data = dplQueryWrapper( cats, args.ignore )
for i = 1, ingredientCount, 4 do
 
data[dataNum] = dplQuery(
 
catParts[1] .. table.concat(
 
ingredients,
 
catParts[2] .. '|' .. catParts[1],
 
i,
 
math.min( i + 3, ingredientCount )
 
) .. catParts[2],
 
args.ignore
 
)
 
dataNum = dataNum + 1
 
end
 
data = table.concat( data )
 
 
end
 
end
 
 

2020년 9월 16일 (수) 00:43 판

[만들기 | 역사 | 캐시 제거]설명문서
이 모듈의 설명문서가 없습니다. 이 모듈의 사용 방법을 안다면, 설명문서를 만들어 주십시오.
local p = {}

local i18n = {
	emptyCategory = '빈 제작법 사용',
	moduleCrafting = [[모듈:Crafting]],
	moduleSlot = [[모듈:Inventory slot]],
	moduleText = [[모듈:Text]],
	queryCategory = '$1을(를) 사용하는 제작법',
	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	
		-- 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