Module:Recipe table

From Box of Rocks WIKI
Revision as of 07:57, 5 November 2020 by Crafting>Dhranios (removing upcoming argument in favor of description with upcoming template (as it is also used for only templates), hope this works)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
This is the documentation page. It will be transcluded into the main module page. See Template:Documentation for more information

This module creates the table for {{crafting}}, {{brewing}} and {{smelting}}. It can only be invoked within other modules.

Usage

The entry point is the table function. The first argument should be whatever arguments you want to pass through, the second argument should be a table of settings.

Setting Use
type What type of recipe, e.g.: 'Crafting'
ingredientArgs A table of the args which contain the ingredients, e.g.: { 'Input' }
outputArgs A table of the args which contain the outputs, e.g.: { 'Output' }
uiFunc The function to call from Module:UI, e.g.: 'craftingTable'

The module returns the wikitext table as the first parameter, and a table of unique ingredients as the second output.

Dependencies




local m = {}

local i18n = {
	headingDescription = 'Description',
	headingIngredients = 'Ingredients',
	headingName = 'Name',
	headingRecipe = '[[$1]] recipe',
	moduleSlot = [[Module:Inventory slot]],
	moduleUi = [[Module:UI]],
	separator = ' +',
	setSeparator = ' or',
	tableDescription = '$1 recipes',
}
local slot = require( i18n.moduleSlot )
local prefixes = slot.i18n.prefixes

--[[Merges a list, or inserts a string
	or table into a table
--]]
local function mergeList( parentTable, content )
	local i = #parentTable + 1
	if content[1] then
		-- Merge list into table
		for _, v in ipairs( content ) do
			parentTable[i] = v
			i = i + 1
		end
	else
		-- Add strings or tables to table
		parentTable[i] = content
	end
end

--[[Loops through the input and output args and parses them into a single table,
	with alias reference data
	
	Identical slots reuse the same table, to allow them to be compared like strings
--]]
local function parseRecipeArgs( args, ingredientArgVals, outputArgs )
	local recipeArgs = {}
	for _, arg in pairs( ingredientArgVals ) do
		recipeArgs[arg] = args[arg]
	end
	for _, arg in pairs( outputArgs ) do
		recipeArgs[arg] = args[arg]
	end
	
	local parsedFrameText = {}
	local parsedRecipeArgs = {}
	for arg, frameText in pairs( recipeArgs ) do
		if frameText then
			local randomise
			for _, oArg in pairs( outputArgs ) do
				if arg == oArg then
					randomise = 'never'
					break
				end
			end
			local frames = not randomise and parsedFrameText[frameText]
			if not frames then
				frames = slot.parseFrameText( frameText, randomise, true )
				parsedFrameText[frameText] = frames
			end
			parsedRecipeArgs[arg] = frames
		end
	end
	
	return parsedRecipeArgs
end

--[[Creates a link (with mod name if specified) with
	any prefix moved outside
--]]
function m.prefixedLink( name, mod )
	local prefix = ''
	for _, thisPrefix in pairs( prefixes ) do
		if name:find( '^' .. thisPrefix .. ' ' ) then
			prefix = thisPrefix .. ' '
			name = name:sub( #prefix + 1 )
			break
		end
	end
	
	local page = name
	if mod then
		page = slot.i18n.modLink:gsub( '%$1', mod ):gsub( '%$2', name )
	end
	
	return table.concat{ prefix, '[[', page, '|', name, ']]' }
end

--[[Creates sets of unique items from a set of slots, using the
	original alias name if available
	
	Each set of items are the frames of that slot
--]]
function m.makeItemSets( argVals, parsedArgs )
	local usedItems = {}
	local function addItem( items, arg, frame, alias )
		if alias then
			frame = alias.frame
		end
		
		local uniqName = ( frame.mod or '' ) .. ':' .. frame.name
		if not usedItems[uniqName] then
			usedItems[uniqName] = true
			items[#items + 1] = frame
		end
		
		return alias and alias.length or 1
	end
	
	local itemSets = {}
	local i = 1
	for _, arg in ipairs( argVals ) do
		local frames = parsedArgs[arg]
		if frames then
			local items = {}
			local frameNum = 1
			while frameNum <= #frames do
				local frame = frames[frameNum]
				if frame[1] then
					local subframeNum = 1
					while subframeNum <= #frame do
						local subframe = frame[subframeNum]
						if subframe.name ~= '' then
							local alias = frame.aliasReference and frame.aliasReference[subframeNum]
							subframeNum = subframeNum + addItem( items, arg, subframe, alias )
						else
							subframeNum = subframeNum + 1
						end
					end
					frameNum = frameNum + 1
				elseif frame.name ~= '' then
					local alias = frames.aliasReference and frames.aliasReference[frameNum]
					frameNum = frameNum + addItem( items, arg, frame, alias )
				else
					frameNum = frameNum + 1
				end
			end
			if #items > 0 then
				itemSets[i] = items
				i = i + 1
			end
		end
	end
	
	return itemSets
end

--[[Creates links for the name/ingredients columns out of
	item sets, with the appropriate separators, and optionally
	"Any" and "Matching" prefixes removed.
--]]
function m.makeItemLinks( itemSets, removePrefixes )
	local links = {}
	for i, itemSet in ipairs( itemSets ) do
		local linkSet = {}
		for i2, item in ipairs( itemSet ) do
			local name = item.name
			if removePrefixes then
				-- Remove prefixes and uppercase first letter
				name = name
					:gsub( '^' .. prefixes.any .. ' ', '' )
					:gsub( '^' .. prefixes.matching .. ' ', '' )
					:gsub( '^%l', string.upper )
			end
			local disjunctionA, disjunctionB = name:match("(.-) or (.+)") -- hardcoding "A or B" names in English
			if disjunctionA then
				linkSet[i2] = m.prefixedLink( disjunctionA, item.mod ) 
				    .. ' or ' 
				    .. m.prefixedLink( disjunctionB, item.mod )
			else
				linkSet[i2] = m.prefixedLink( name, item.mod )
			end
		end
		links[i] = table.concat( linkSet, i18n.setSeparator .. '<br>' )
	end
	
	return table.concat( links, i18n.separator .. '<br>' )
end

-- Creates the table header
function m.makeHeader( recipeType, class, showName, showDescription, multirow )
	class = class or ''
	
	local nameCell = ''
	if showName then
		nameCell = i18n.headingName .. '!!'
	end
	
	local descriptionCell = ''
	if showDescription then
		descriptionCell = '!!class="unsortable"|' .. i18n.headingDescription
	end
	
	local recipeAttribs = ''
	if multirow then
		class = 'sortable collapsible ' .. class
		recipeAttribs = 'class="unsortable"|'
	end
	
	local header = table.concat( {
		' {| class="wikitable ' .. class .. '" data-description="' .. i18n.tableDescription:gsub( '%$1', recipeType ) .. '"',
		'!' .. nameCell ..
		i18n.headingIngredients .. '!!' ..
		recipeAttribs .. i18n.headingRecipe:gsub( '%$1', recipeType ) ..
		descriptionCell
	}, '\n' )
	return header
end

-- Create the contents for the name cell
function m.makeNameCell( name, outputArgs, parsedRecipeArgs )
	local cell = {}
	if name then
		cell[1] = name
	else
		cell[1] = m.makeItemLinks( m.makeItemSets( outputArgs, parsedRecipeArgs ), true )
	end
	
	return table.concat( cell, '<br>' )
end

-- Create the contents for the ingredients cell
function m.makeIngredientsCell( ingredients, itemSets )
	return ingredients or m.makeItemLinks( itemSets )
end

--[[Main entry point, creates the table with the relevant DPL vars
	to allow multiple table rows from separate template calls
	
	Also returns the unique ingredients, for categorisation purposes in
	Module:Crafting
--]]
function m.table( args, settings )
	local f = mw.getCurrentFrame()
	
	local multirow = f:callParserFunction( '#dplvar', 'recipetable-multirow' )
	if multirow == '' then
		multirow = nil
	end
	
	local showHead = args.head
	local showFoot = args.foot
	if multirow then
		showHead = nil
	elseif showHead and not showFoot then
		multirow = true
		f:callParserFunction( '#dplvar:set', 'recipetable-multirow', '1' )
	else
		showHead = true
		showFoot = true
	end
		
	local showName = args.showname
	local showDescription = args.showdescription
	if multirow then
		if showHead then
			showName = args.showname or '1'
			f:callParserFunction( '#dplvar:set', 'recipetable-name', showName, 'recipetable-description', showDescription )
		else
			showName = f:callParserFunction( '#dplvar', 'recipetable-name' )
			showDescription = f:callParserFunction( '#dplvar', 'recipetable-description' )
		end
	end
	if showName ~= '1' then
		showName = nil
	end
	if showDescription == '' then
		showDescription = nil
	end
	
	local out = {}
	
	if showHead then
		out[1] = m.makeHeader( settings.type, args.class, showName, showDescription, multirow )
	end
	
	local ingredientArgVals = settings.ingredientArgs
	local outputArgs = settings.outputArgs
	
	local parsedRecipeArgs = args
	if not args.parsed then
		parsedRecipeArgs = parseRecipeArgs( args, ingredientArgVals, outputArgs )
	end
	
	local cells = {}
	
	if showName then
		cells[1] = '!' .. m.makeNameCell( args.name, outputArgs, parsedRecipeArgs )
	end
	
	local ingredientsItemSets = m.makeItemSets( ingredientArgVals, parsedRecipeArgs )
	cells[#cells + 1] = '|' .. m.makeIngredientsCell( args.ingredients, ingredientsItemSets )
	
	cells[#cells + 1] = '|style="padding:1px;text-align:center"|' .. require( i18n.moduleUi )[settings.uiFunc]( args )
	
	if showDescription then
		cells[#cells + 1] = '|' .. ( args.description or '' )
	end
	
	out[#out + 1] = table.concat( cells, '\n' )
	
	out[#out + 1] = showFoot and '|}' or ''
	if showFoot then
		f:callParserFunction( '#dplvar:set',
			'recipetable-multirow', '',
			'recipetable-name', '',
			'recipetable-description', ''
		)
	end
	
	return table.concat( out, '\n|-\n' ), ingredientsItemSets
end

return m